Files
rogaining_srv/migrate_all_events_with_gps_final.py
2025-08-29 09:11:20 +09:00

408 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
"""
old_rogdb から rogdb への全イベントデータ移行スクリプトGPS情報含む
FC岐阜の成功事例をベースに全てのイベントのteam/member/entry + GPS情報を移行
"""
import os
import sys
import django
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()
from django.db import transaction, connection
from rog.models import NewEvent2, Team, Entry, Member, NewCategory, CustomUser
import psycopg2
print("📋 全イベントデータ移行スクリプトGPS情報含むを開始します")
# 各データベース接続設定
OLD_DB_CONFIG = {
'host': 'postgres-db',
'port': 5432,
'database': 'old_rogdb',
'user': 'postgres',
'password': 'password'
}
GIFUROGE_DB_CONFIG = {
'host': 'postgres-db',
'port': 5432,
'database': 'gifuroge',
'user': 'postgres',
'password': 'password'
}
try:
# データベース接続
old_conn = psycopg2.connect(**OLD_DB_CONFIG)
gifuroge_conn = psycopg2.connect(**GIFUROGE_DB_CONFIG)
with transaction.atomic():
# === STEP 1: ユーザー確認 ===
print("\n=== STEP 1: ユーザー確認 ===")
admin_user, created = CustomUser.objects.get_or_create(
username='admin',
defaults={
'email': 'admin@example.com',
'is_staff': True,
'is_superuser': True
}
)
print(f"管理ユーザー: {'作成' if created else '既存'}")
# === STEP 2: イベントとカテゴリー情報取得 ===
print("\n=== STEP 2: 既存イベント・カテゴリー確認 ===")
existing_events = list(NewEvent2.objects.values_list('id', 'name'))
print(f"既存イベント数: {len(existing_events)}")
if not existing_events:
print("❌ イベントが存在しません。先にイベントを作成してください。")
sys.exit(1)
existing_categories = list(NewCategory.objects.values_list('id', 'name'))
print(f"既存カテゴリー数: {len(existing_categories)}")
if not existing_categories:
print("❌ カテゴリーが存在しません。先にカテゴリーを作成してください。")
sys.exit(1)
# === STEP 3: チーム移行 ===
print("\n=== STEP 3: チーム移行 ===")
with old_conn.cursor() as cursor:
cursor.execute("""
SELECT id, name, owner_id, event_id, reg_date,
representative_name, representative_phone,
representative_email, is_deleted
FROM team
WHERE is_deleted = FALSE
ORDER BY id;
""")
old_teams = cursor.fetchall()
print(f"old_rogdbのチーム数: {len(old_teams)}")
total_migrated_teams = 0
for team_data in old_teams:
old_team_id, name, owner_id, event_id, reg_date, rep_name, rep_phone, rep_email, is_deleted = team_data
# イベントが存在するかチェック
if not NewEvent2.objects.filter(id=event_id).exists():
continue
# チームが既に存在するかチェック
if Team.objects.filter(id=old_team_id).exists():
continue
try:
team = Team.objects.create(
id=old_team_id,
name=name,
owner_id=owner_id or admin_user.id,
event_id=event_id,
reg_date=reg_date,
representative_name=rep_name or name,
representative_phone=rep_phone or '',
representative_email=rep_email or '',
is_deleted=False
)
total_migrated_teams += 1
if total_migrated_teams <= 5:
print(f" チーム作成: {name} (ID: {old_team_id})")
except Exception as e:
print(f" ❌ チーム作成エラー: {name} - {e}")
print(f"✅ チーム移行完了: {total_migrated_teams}件作成")
# === STEP 4: メンバー移行 ===
print("\n=== STEP 4: メンバー移行 ===")
with old_conn.cursor() as cursor:
cursor.execute("""
SELECT id, team_id, name, kana, is_leader,
phone, email, birthday, gender, si_number, is_deleted
FROM member
WHERE is_deleted = FALSE
ORDER BY id;
""")
old_members = cursor.fetchall()
print(f"old_rogdbのメンバー数: {len(old_members)}")
total_migrated_members = 0
for member_data in old_members:
old_member_id, team_id, name, kana, is_leader, phone, email, birthday, gender, si_number, is_deleted = member_data
# チームが存在するかチェック
if not Team.objects.filter(id=team_id).exists():
continue
# メンバーが既に存在するかチェック
if Member.objects.filter(id=old_member_id).exists():
continue
try:
member = Member.objects.create(
id=old_member_id,
team_id=team_id,
name=name,
kana=kana or '',
is_leader=is_leader or False,
phone=phone or '',
email=email or '',
birthday=birthday,
gender=gender or '',
si_number=si_number,
is_deleted=False
)
total_migrated_members += 1
if total_migrated_members <= 5:
print(f" メンバー作成: {name} (チーム{team_id})")
except Exception as e:
print(f" ❌ メンバー作成エラー: {name} - {e}")
print(f"✅ メンバー移行完了: {total_migrated_members}件作成")
# === STEP 5: エントリー移行 ===
print("\n=== STEP 5: エントリー移行 ===")
total_migrated_entries = 0
# イベント別にエントリーを移行
for event_id, event_name in existing_events:
print(f"\n 📊 {event_name} (ID: {event_id}) のエントリー移行中...")
# カテゴリーを取得(なければデフォルト使用)
cat_id = existing_categories[0][0] if existing_categories else 1
with old_conn.cursor() as cursor:
cursor.execute("""
SELECT t.id as team_id, t.name as team_name, t.owner_id,
s.zekken_number, s.label, s.is_deleted
FROM team t
LEFT JOIN start s ON t.id = s.team_id
WHERE t.event_id = %s AND t.is_deleted = FALSE
ORDER BY t.id;
""", [event_id])
entries_data = cursor.fetchall()
print(f" 対象エントリー数: {len(entries_data)}")
event_migrated_entries = 0
for entry_data in entries_data:
team_id, team_name, owner_id, zekken, label, is_deleted = entry_data
# エントリーが既に存在するかチェック
if Entry.objects.filter(team_id=team_id, event_id=event_id).exists():
continue
try:
# チームとイベントの存在確認
team_obj = Team.objects.get(id=team_id)
event_obj = NewEvent2.objects.get(id=event_id)
# Entryオブジェクト作成
entry = Entry.objects.create(
date=event_obj.start_datetime,
category_id=cat_id,
event_id=event_id,
owner_id=owner_id or admin_user.id,
team_id=team_id,
is_active=True,
zekken_number=int(zekken) if zekken else 0,
hasGoaled=False,
hasParticipated=False,
zekken_label=label or f"{event_name}-{zekken}",
is_trial=False,
staff_privileges=False,
can_access_private_events=False,
team_validation_status='approved'
)
event_migrated_entries += 1
total_migrated_entries += 1
if event_migrated_entries <= 3:
print(f" エントリー作成: {team_name} - ゼッケン{zekken}")
except Team.DoesNotExist:
print(f" ❌ チーム{team_id}が見つかりません: {team_name}")
except NewEvent2.DoesNotExist:
print(f" ❌ イベント{event_id}が見つかりません")
except Exception as e:
print(f" ❌ エントリー作成エラー: {team_name} - {e}")
print(f"{event_name}: {event_migrated_entries}件のエントリーを移行")
print(f"\n✅ 全エントリー移行完了: {total_migrated_entries}件作成")
# === STEP 6: GPS情報移行 ===
print("\n=== STEP 6: GPS情報通過データ移行 ===")
with gifuroge_conn.cursor() as gifuroge_cursor:
# GPS情報データ数確認
gifuroge_cursor.execute("SELECT COUNT(*) FROM gps_information;")
gps_total_count = gifuroge_cursor.fetchone()[0]
print(f"GPS情報総数: {gps_total_count}")
if gps_total_count > 0:
# ロガインDBからteam_idとzekken_numberの対応関係を取得
print("\n 📊 チーム-ゼッケン対応表作成中...")
team_zekken_map = {}
with old_conn.cursor() as old_cursor:
old_cursor.execute("""
SELECT t.id as team_id, s.zekken_number, t.event_id
FROM team t
LEFT JOIN start s ON t.id = s.team_id
WHERE t.is_deleted = FALSE AND s.zekken_number IS NOT NULL;
""")
team_zekken_data = old_cursor.fetchall()
for team_id, zekken_number, event_id in team_zekken_data:
if zekken_number:
team_zekken_map[str(zekken_number)] = {
'team_id': team_id,
'event_id': event_id
}
print(f" チーム-ゼッケン対応: {len(team_zekken_map)}")
# GPS情報をバッチで移行
print("\n 🌍 GPS情報移行中...")
# 既存のGPS情報をクリア必要に応じて
with connection.cursor() as django_cursor:
django_cursor.execute("SELECT COUNT(*) FROM gps_checkins;")
existing_gps = django_cursor.fetchone()[0]
print(f" 既存GPS記録: {existing_gps}")
# GPS情報を取得・移行
gifuroge_cursor.execute("""
SELECT zekken_number, event_code, cp_number, lattitude, longitude,
image_address, image_receipt, image_qr, validate_location,
goal_time, late_point, create_at, create_user, update_at,
update_user, buy_flag, colabo_company_memo, points
FROM gps_information
ORDER BY create_at;
""")
gps_records = gifuroge_cursor.fetchall()
print(f" 移行対象GPS記録: {len(gps_records)}")
migrated_gps_count = 0
batch_size = 1000
with connection.cursor() as django_cursor:
for i in range(0, len(gps_records), batch_size):
batch = gps_records[i:i+batch_size]
print(f" バッチ {i//batch_size + 1}: {len(batch)}件処理中...")
for gps_record in batch:
(zekken_number, event_code, cp_number, lattitude, longitude,
image_address, image_receipt, image_qr, validate_location,
goal_time, late_point, create_at, create_user, update_at,
update_user, buy_flag, colabo_company_memo, points) = gps_record
# zekken_numberから対応するteam_idを取得
team_info = team_zekken_map.get(str(zekken_number))
team_id = team_info['team_id'] if team_info else None
event_id = team_info['event_id'] if team_info else None
try:
# gps_checkinsテーブルに実際の構造に合わせて挿入
django_cursor.execute("""
INSERT INTO gps_checkins (
path_order, zekken_number, event_code, cp_number,
lattitude, longitude, image_address, image_receipt,
image_qr, validate_location, goal_time, late_point,
create_at, create_user, update_at, update_user,
buy_flag, colabo_company_memo, points, event_id,
team_id, validation_status
) VALUES (
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s
);
""", [
0, # path_orderデフォルト値
str(zekken_number), # zekken_number
event_code, # event_code
cp_number, # cp_number
lattitude, # lattitude
longitude, # longitude
image_address, # image_address
image_receipt, # image_receipt
bool(image_qr) if image_qr is not None else False, # image_qr
bool(validate_location) if validate_location is not None else False, # validate_location
goal_time, # goal_time
late_point, # late_point
create_at, # create_at
create_user, # create_user
update_at, # update_at
update_user, # update_user
bool(buy_flag) if buy_flag is not None else False, # buy_flag
colabo_company_memo or '', # colabo_company_memo
points, # points
event_id, # event_id
team_id, # team_id
'pending' # validation_statusデフォルト値
])
migrated_gps_count += 1
except Exception as e:
if migrated_gps_count < 5: # 最初の5件のエラーのみ表示
print(f" ❌ GPS記録移行エラー: ゼッケン{zekken_number} - {e}")
# バッチごとにコミット
connection.commit()
print(f" ✅ GPS情報移行完了: {migrated_gps_count}件作成")
else:
print(" 📍 GPS情報が存在しません")
old_conn.close()
gifuroge_conn.close()
# === 最終確認 ===
print("\n=== 移行結果確認 ===")
total_teams = Team.objects.count()
total_members = Member.objects.count()
total_entries = Entry.objects.count()
# GPS情報確認
with connection.cursor() as cursor:
cursor.execute("SELECT COUNT(*) FROM gps_checkins;")
total_gps = cursor.fetchone()[0]
print(f"総チーム数: {total_teams}")
print(f"総メンバー数: {total_members}")
print(f"総エントリー数: {total_entries}")
print(f"総GPS記録数: {total_gps}")
# イベント別エントリー統計
print("\n=== イベント別エントリー統計 ===")
for event_id, event_name in existing_events[:10]:
entry_count = Entry.objects.filter(event_id=event_id).count()
if entry_count > 0:
print(f" {event_name}: {entry_count}")
print("\n🎉 全イベントデータ移行GPS情報含むが完了しました")
print("🎯 通過審査管理画面で全てのイベントのゼッケン番号が表示され、")
print(" GPS情報による通過データも利用可能になります。")
except Exception as e:
print(f"❌ エラーが発生しました: {e}")
import traceback
traceback.print_exc()