Fix migration error

This commit is contained in:
2025-08-29 09:11:20 +09:00
parent a180c1e258
commit b91b522fa3
26 changed files with 5848 additions and 22 deletions

300
migrate_gps_information.py Normal file
View File

@ -0,0 +1,300 @@
#!/usr/bin/env python3
"""
GPS情報通過データ移行スクリプト
gifurogeのgps_informationテーブルから新しいrogdbシステムに通過データを移行
"""
import os
import sys
import django
from datetime import datetime
import psycopg2
from django.utils import timezone
from django.db import transaction
# Django設定
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()
from rog.models import (
GpsLog, GpsCheckin, CheckinExtended, Entry, NewEvent2,
CustomUser, Team, Waypoint, Location2025
)
class GpsInformationMigrator:
def __init__(self):
# 環境変数から接続情報を取得
self.gifuroge_conn_params = {
'host': os.environ.get('PG_HOST', 'postgres-db'),
'database': 'gifuroge',
'user': os.environ.get('POSTGRES_USER', 'postgres'),
'password': os.environ.get('POSTGRES_PASS', 'password'),
'port': os.environ.get('PG_PORT', 5432),
}
# 統計情報
self.stats = {
'total_gps_info': 0,
'migrated_gps_logs': 0,
'migrated_checkins': 0,
'skipped_records': 0,
'errors': 0,
'error_details': []
}
def connect_to_gifuroge(self):
"""gifurogeデータベースに接続"""
try:
conn = psycopg2.connect(**self.gifuroge_conn_params)
return conn
except Exception as e:
print(f"❌ gifurogeデータベース接続エラー: {e}")
return None
def get_gps_information_data(self):
"""gifurogeのgps_informationデータを取得"""
conn = self.connect_to_gifuroge()
if not conn:
return []
try:
cursor = conn.cursor()
# まずテーブル構造を確認
cursor.execute("""
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'gps_information'
AND table_schema = 'public'
ORDER BY ordinal_position;
""")
columns = cursor.fetchall()
print("=== gps_information テーブル構造 ===")
for col in columns:
print(f"- {col[0]}: {col[1]}")
# データ数確認
cursor.execute("SELECT COUNT(*) FROM gps_information;")
total_count = cursor.fetchone()[0]
self.stats['total_gps_info'] = total_count
print(f"\n📊 gps_information 総レコード数: {total_count}")
if total_count == 0:
print("⚠️ gps_informationテーブルにデータがありません")
return []
# 全データを取得(テーブル構造に合わせて修正)
cursor.execute("""
SELECT
serial_number, zekken_number, event_code, cp_number,
image_address, goal_time, late_point,
create_at, create_user, update_at, update_user,
buy_flag, minus_photo_flag, colabo_company_memo
FROM gps_information
ORDER BY create_at, serial_number;
""")
data = cursor.fetchall()
print(f"{len(data)}件のgps_informationデータを取得しました")
return data
except Exception as e:
print(f"❌ データ取得エラー: {e}")
self.stats['errors'] += 1
self.stats['error_details'].append(f"データ取得エラー: {e}")
return []
finally:
if conn:
conn.close()
def find_matching_entry(self, zekken_number, event_code):
"""ゼッケン番号とイベントコードからEntryを検索"""
try:
# NewEvent2でイベントを検索
events = NewEvent2.objects.filter(event_name__icontains=event_code)
if not events.exists():
# イベントコードの部分一致で検索
events = NewEvent2.objects.filter(
event_name__icontains=event_code.replace('_', ' ')
)
for event in events:
# ゼッケン番号でEntryを検索
entries = Entry.objects.filter(
event=event,
zekken_number=zekken_number
)
if entries.exists():
return entries.first()
# 見つからない場合はNone
return None
except Exception as e:
print(f"⚠️ Entry検索エラー (ゼッケン: {zekken_number}, イベント: {event_code}): {e}")
return None
def find_matching_location(self, cp_number):
"""CP番号からLocationを検索"""
try:
if not cp_number:
return None
# Location2025から検索
locations = Location2025.objects.filter(cp_number=cp_number)
if locations.exists():
return locations.first()
# 部分一致で検索
locations = Location2025.objects.filter(cp_number__icontains=str(cp_number))
if locations.exists():
return locations.first()
return None
except Exception as e:
print(f"⚠️ Location検索エラー (CP: {cp_number}): {e}")
return None
def migrate_gps_record(self, record):
"""個別のGPS記録を移行"""
try:
(serial_number, zekken_number, event_code, cp_number,
image_address, goal_time, late_point,
create_at, create_user, update_at, update_user,
buy_flag, minus_photo_flag, colabo_company_memo) = record
# checkin_timeはcreate_atを使用
checkin_time = create_at or timezone.now()
# Entryを検索
entry = self.find_matching_entry(zekken_number, event_code)
if not entry:
print(f"⚠️ Entry未発見: ゼッケン{zekken_number}, イベント{event_code}")
self.stats['skipped_records'] += 1
return False
# Locationを検索オプション
location = self.find_matching_location(cp_number) if cp_number else None
# 既存のGpsLogをチェック
existing_log = GpsLog.objects.filter(
zekken_number=str(zekken_number),
event_code=event_code,
checkin_time=checkin_time
).first()
if existing_log:
print(f"⚠️ 既存記録をスキップ: ゼッケン{zekken_number}, {checkin_time}")
self.stats['skipped_records'] += 1
return False
# GpsLogを作成
gps_log = GpsLog.objects.create(
serial_number=serial_number or 0,
zekken_number=str(zekken_number),
event_code=event_code,
cp_number=str(cp_number) if cp_number else '',
image_address=image_address or '',
checkin_time=checkin_time,
goal_time=goal_time or '',
late_point=late_point or 0,
create_at=create_at or timezone.now(),
create_user=create_user or '',
update_at=update_at or timezone.now(),
update_user=update_user or '',
buy_flag=buy_flag or False,
minus_photo_flag=minus_photo_flag or False,
colabo_company_memo=colabo_company_memo or '',
is_service_checked=False, # デフォルト値
score=0, # デフォルト値
scoreboard_url='' # デフォルト値
)
self.stats['migrated_gps_logs'] += 1
# CheckinExtendedも作成通過記録として
if cp_number and location:
try:
checkin_extended = CheckinExtended.objects.create(
entry=entry,
location=location,
checkin_time=checkin_time,
image_url=image_address or '',
score_override=0, # デフォルト値
notes=f"移行データ: {colabo_company_memo}",
is_verified=False # デフォルト値
)
self.stats['migrated_checkins'] += 1
print(f"✅ チェックイン記録作成: ゼッケン{zekken_number}, CP{cp_number}")
except Exception as e:
print(f"⚠️ CheckinExtended作成エラー: {e}")
print(f"✅ GPS記録移行完了: ゼッケン{zekken_number}, {checkin_time}")
return True
except Exception as e:
print(f"❌ GPS記録移行エラー: {e}")
self.stats['errors'] += 1
self.stats['error_details'].append(f"GPS記録移行エラー: {e}")
return False
def run_migration(self):
"""メイン移行処理"""
print("🚀 GPS情報移行スクリプト開始")
print("=" * 50)
# gifurogeからデータを取得
gps_data = self.get_gps_information_data()
if not gps_data:
print("❌ 移行するデータがありません")
return
print(f"\n📋 {len(gps_data)}件のGPS記録の移行を開始...")
# バッチ処理で移行
batch_size = 100
total_batches = (len(gps_data) + batch_size - 1) // batch_size
for batch_num in range(total_batches):
start_idx = batch_num * batch_size
end_idx = min(start_idx + batch_size, len(gps_data))
batch_data = gps_data[start_idx:end_idx]
print(f"\n📦 バッチ {batch_num + 1}/{total_batches} ({len(batch_data)}件) 処理中...")
with transaction.atomic():
for record in batch_data:
self.migrate_gps_record(record)
# 統計レポート
self.print_migration_report()
def print_migration_report(self):
"""移行結果レポート"""
print("\n" + "=" * 50)
print("📊 GPS情報移行完了レポート")
print("=" * 50)
print(f"📋 総GPS記録数: {self.stats['total_gps_info']}")
print(f"✅ 移行済みGpsLog: {self.stats['migrated_gps_logs']}")
print(f"✅ 移行済みCheckin: {self.stats['migrated_checkins']}")
print(f"⚠️ スキップ記録: {self.stats['skipped_records']}")
print(f"❌ エラー数: {self.stats['errors']}")
if self.stats['error_details']:
print("\n❌ エラー詳細:")
for error in self.stats['error_details'][:10]: # 最初の10個だけ表示
print(f" - {error}")
if len(self.stats['error_details']) > 10:
print(f" ... 他 {len(self.stats['error_details']) - 10}")
success_rate = (self.stats['migrated_gps_logs'] / max(self.stats['total_gps_info'], 1)) * 100
print(f"\n📈 移行成功率: {success_rate:.1f}%")
print("=" * 50)
def main():
"""メイン実行関数"""
migrator = GpsInformationMigrator()
migrator.run_migration()
if __name__ == '__main__':
main()