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

829 lines
42 KiB
Python
Raw 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を移行
さらに、gifurogeのgps_informationをrogdbのrog_checkinsに移行
"""
import os
import sys
import django
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()
from django.db import transaction
from rog.models import NewEvent2, Team, Entry, NewCategory, CustomUser, Member
import psycopg2
from collections import defaultdict
from datetime import datetime, timedelta
import pytz
print("=== old_rogdb から 全イベントデータ移行GPS情報付き ===")
# GPS情報移行用のヘルパー関数
def load_event_dates_from_db():
"""gifurogeのevent_tableからイベントコードと日付のマッピングを取得"""
event_dates = {}
try:
# gifuroge データベースに接続
conn = psycopg2.connect(
host='postgres-db',
database='gifuroge',
user='admin',
password='admin123456'
)
cursor = conn.cursor()
# event_tableからイベントコードと開始日・終了日を取得
cursor.execute("""
SELECT event_code, event_day, end_day
FROM event_table
WHERE event_code IS NOT NULL AND event_day IS NOT NULL
ORDER BY event_day
""")
events = cursor.fetchall()
for event_code, event_day, end_day in events:
# デバッグ用:読み込まれた生データを表示
print(f"🔍 生データ: {event_code} | event_day={event_day}({type(event_day)}) | end_day={end_day}({type(end_day)})")
# event_dayの日付フォーマットを統一yyyy-mm-dd形式に変換
start_date = None
end_date = None
# event_day開始日の処理
if isinstance(event_day, str):
if '/' in event_day:
start_date = normalize_date_format(event_day.replace('/', '-'))
elif '-' in event_day:
start_date = normalize_date_format(event_day)
else:
date_part = event_day.split(' ')[0] if ' ' in event_day else event_day
start_date = normalize_date_format(date_part.replace('/', '-'))
else:
start_date = normalize_date_format(event_day.strftime('%Y-%m-%d'))
# end_day終了日の処理
if end_day:
if isinstance(end_day, str):
if '/' in end_day:
end_date = normalize_date_format(end_day.replace('/', '-'))
elif '-' in end_day:
end_date = normalize_date_format(end_day)
else:
date_part = end_day.split(' ')[0] if ' ' in end_day else end_day
end_date = normalize_date_format(date_part.replace('/', '-'))
else:
end_date = normalize_date_format(end_day.strftime('%Y-%m-%d'))
else:
# end_dayが設定されていない場合は、event_dayと同じ日とする
end_date = start_date
# イベント期間情報を保存
event_dates[event_code] = {
'start_date': start_date,
'end_date': end_date,
'display_date': start_date # 主要な表示用日付
}
conn.close()
print(f"📅 event_tableから{len(event_dates)}件のイベント情報を読み込みました:")
for code, date_info in event_dates.items():
if date_info['start_date'] == date_info['end_date']:
print(f" {code}: {date_info['start_date']}")
else:
print(f" {code}: {date_info['start_date']} - {date_info['end_date']}")
except Exception as e:
print(f"⚠️ event_table読み込みエラー: {e}")
# フォールバック用のデフォルト値
event_dates = {
'gifu2024': {'start_date': '2024-10-27', 'end_date': '2024-10-27', 'display_date': '2024-10-27'},
'gifu2023': {'start_date': '2023-11-12', 'end_date': '2023-11-12', 'display_date': '2023-11-12'},
'gifu2022': {'start_date': '2022-11-13', 'end_date': '2022-11-13', 'display_date': '2022-11-13'},
'test2024': {'start_date': '2024-12-15', 'end_date': '2024-12-15', 'display_date': '2024-12-15'},
'test2025': {'start_date': '2025-01-25', 'end_date': '2025-01-25', 'display_date': '2025-01-25'},
'郡上': {'start_date': '2024-06-15', 'end_date': '2024-06-15', 'display_date': '2024-06-15'}
}
print(f"デフォルトのイベント日付を使用します: {len(event_dates)}")
return event_dates
def get_event_date(event_code, event_dates_cache):
"""イベントコードから日付を取得(キャッシュ使用)"""
if event_code in event_dates_cache:
return event_dates_cache[event_code]['display_date']
# 未知のイベントコードの場合、警告を出してデフォルト日付を返す
print(f"⚠️ 未知のイベントコード '{event_code}' - デフォルト日付2024-01-01を使用")
return '2024-01-01' # デフォルト日付
def normalize_date_format(date_str):
"""日付文字列をyyyy-mm-dd形式に正規化"""
try:
# datetimeオブジェクトの場合
if hasattr(date_str, 'strftime'):
return date_str.strftime('%Y-%m-%d')
# 文字列の場合
if isinstance(date_str, str):
# スラッシュ区切りをハイフン区切りに変換
if '/' in date_str:
date_str = date_str.replace('/', '-')
# yyyy-m-d や yyyy-mm-d などを yyyy-mm-dd に正規化
parts = date_str.split('-')
if len(parts) == 3:
year, month, day = parts
return f"{year}-{month.zfill(2)}-{day.zfill(2)}"
return date_str
except:
return date_str
def is_within_event_period(gps_datetime, event_code, event_dates_cache):
"""GPS記録の日時がイベント期間内かチェック"""
if event_code not in event_dates_cache:
return True # 未知のイベントの場合は通す
event_info = event_dates_cache[event_code]
start_date = normalize_date_format(event_info['start_date'])
end_date = normalize_date_format(event_info['end_date'])
try:
# GPS記録の日付部分を取得して正規化
gps_date = normalize_date_format(gps_datetime.strftime('%Y-%m-%d'))
# イベント期間内かチェック
return start_date <= gps_date <= end_date
except Exception as e:
print(f"日付比較エラー: GPS={gps_datetime}, イベント={event_code}, エラー={e}")
return True # エラーの場合は通す
def parse_goal_time(goal_time_str, event_date):
"""ゴール時刻をパース"""
if not goal_time_str or not event_date:
return None
try:
# HH:MM形式からdatetimeに変換
time_parts = goal_time_str.split(':')
if len(time_parts) == 2:
hour, minute = int(time_parts[0]), int(time_parts[1])
event_datetime = datetime.strptime(event_date, '%Y-%m-%d')
goal_datetime = event_datetime.replace(hour=hour, minute=minute, second=0, microsecond=0)
# JST timezone設定
jst = pytz.timezone('Asia/Tokyo')
return jst.localize(goal_datetime)
except Exception as e:
print(f"ゴール時刻パースエラー: {goal_time_str} - {e}")
return None
def convert_utc_to_jst(utc_datetime):
"""UTC時刻をJSTに変換"""
if not utc_datetime:
return None
try:
if isinstance(utc_datetime, str):
utc_datetime = datetime.fromisoformat(utc_datetime.replace('Z', '+00:00'))
# UTCとして扱い、JSTに変換
if utc_datetime.tzinfo is None:
utc = pytz.UTC
utc_datetime = utc.localize(utc_datetime)
jst = pytz.timezone('Asia/Tokyo')
return utc_datetime.astimezone(jst)
except Exception as e:
print(f"時刻変換エラー: {utc_datetime} - {e}")
return None
def migrate_gps_data():
"""GPS情報をgifurogeからrogdbに移行"""
print("\n=== GPS情報移行開始 ===")
# まず、イベント日付情報を読み込み
event_dates_cache = load_event_dates_from_db()
try:
# gifuroge データベースに接続
gifuroge_conn = psycopg2.connect(
host='postgres-db',
database='gifuroge',
user='admin',
password='admin123456'
)
# rogdb データベースに接続
rogdb_conn = psycopg2.connect(
host='postgres-db',
database='rogdb',
user='admin',
password='admin123456'
)
print("✅ GPS移行用データベース接続成功")
with gifuroge_conn.cursor() as source_cursor, rogdb_conn.cursor() as target_cursor:
# 既存のGPSチェックイン記録をクリア
target_cursor.execute("DELETE FROM rog_gpscheckin;")
print("既存のGPSチェックイン記録をクリアしました")
# GPS記録を取得serial_number < 20000のみ、実際のGPS記録
source_cursor.execute("""
SELECT serial_number, zekken_number, event_code, cp_number, create_at, goal_time
FROM gps_information
WHERE serial_number < 20000
ORDER BY serial_number
""")
gps_records = source_cursor.fetchall()
print(f"移行対象GPS記録数: {len(gps_records)}")
success_count = 0
skip_count = 0
error_count = 0
event_stats = defaultdict(set)
skip_stats = defaultdict(int) # スキップ統計
skip_reasons = defaultdict(int) # スキップ理由別統計
large_skip_events = set() # 大量スキップイベントの詳細分析用
skip_date_ranges = defaultdict(list) # スキップされたGPS日付の範囲集計用
for record in gps_records:
serial_number, zekken, event_code, cp_number, create_at, goal_time = record
try:
# イベント日付取得(キャッシュから)
event_date = get_event_date(event_code, event_dates_cache)
# event_dateはNoneを返さなくなったので、この条件は不要だが安全のため残す
if not event_date:
# 時刻変換してGPS日付を取得
jst_create_at = convert_utc_to_jst(create_at)
gps_date = jst_create_at.strftime('%Y-%m-%d') if jst_create_at else 'N/A'
print(f"⚠️ イベント日付取得失敗: {event_code} GPS日付:{gps_date}")
skip_count += 1
skip_stats[event_code] += 1
skip_reasons["イベント日付取得失敗"] += 1
continue
# 時刻変換
jst_create_at = convert_utc_to_jst(create_at)
jst_goal_time = parse_goal_time(goal_time, event_date) if goal_time else None
if not jst_create_at:
print(f"時刻変換失敗: {serial_number}")
error_count += 1
skip_stats[event_code] += 1
skip_reasons["時刻変換失敗"] += 1
continue
# 未知のイベントコードの場合はGPS日付も表示
if event_code not in event_dates_cache:
gps_date = jst_create_at.strftime('%Y-%m-%d')
print(f"⚠️ 未知のイベントコード '{event_code}' GPS日付:{gps_date} - デフォルト日付2024-01-01を使用")
# GPS記録がイベント期間内かチェック
if not is_within_event_period(jst_create_at, event_code, event_dates_cache):
# GPS日付を正規化期間外スキップ用
gps_date = normalize_date_format(jst_create_at.strftime('%Y-%m-%d'))
# 大量スキップイベントの詳細分析
should_show_detail = (skip_count < 10 or
(event_code in ['各務原', '岐阜市', '養老ロゲ', '郡上', '大垣2', 'test下呂'] and
skip_stats[event_code] < 5))
if should_show_detail:
event_info = event_dates_cache.get(event_code, {})
start_date = normalize_date_format(event_info.get('start_date', 'N/A'))
end_date = normalize_date_format(event_info.get('end_date', 'N/A'))
# 600件超のイベントは特別扱い
if event_code in ['各務原', '岐阜市', '養老ロゲ', '郡上', '大垣2', 'test下呂']:
large_skip_events.add(event_code)
print(f"🔍 大量スキップイベント詳細分析 - {event_code}:")
print(f" イベントコード: {event_code}")
print(f" GPS元時刻: {create_at}")
print(f" GPS JST時刻: {jst_create_at}")
print(f" GPS日付(正規化前): {jst_create_at.strftime('%Y-%m-%d')}")
print(f" GPS日付(正規化後): {gps_date}")
print(f" イベント開始日(正規化前): {event_info.get('start_date', 'N/A')}")
print(f" イベント開始日(正規化後): {start_date}")
print(f" イベント終了日(正規化前): {event_info.get('end_date', 'N/A')}")
print(f" イベント終了日(正規化後): {end_date}")
print(f" 比較結果: {start_date} <= {gps_date} <= {end_date}")
print(f" 文字列比較1: '{start_date}' <= '{gps_date}' = {start_date <= gps_date}")
print(f" 文字列比較2: '{gps_date}' <= '{end_date}' = {gps_date <= end_date}")
print(f" 年差: GPS年={gps_date[:4]}, イベント年={start_date[:4]}")
else:
# デバッグ情報を追加
print(f"🔍 デバッグ情報:")
print(f" イベントコード: {event_code}")
print(f" GPS元時刻: {create_at}")
print(f" GPS JST時刻: {jst_create_at}")
print(f" GPS日付(正規化前): {jst_create_at.strftime('%Y-%m-%d')}")
print(f" GPS日付(正規化後): {gps_date}")
print(f" イベント開始日(正規化前): {event_info.get('start_date', 'N/A')}")
print(f" イベント開始日(正規化後): {start_date}")
print(f" イベント終了日(正規化前): {event_info.get('end_date', 'N/A')}")
print(f" イベント終了日(正規化後): {end_date}")
print(f" 比較結果: {start_date} <= {gps_date} <= {end_date}")
print(f" 文字列比較1: '{start_date}' <= '{gps_date}' = {start_date <= gps_date}")
print(f" 文字列比較2: '{gps_date}' <= '{end_date}' = {gps_date <= end_date}")
print(f"期間外GPS記録スキップ: {event_code} GPS日付:{gps_date} イベント期間:{start_date}-{end_date}")
# 大量スキップイベントのGPS日付を記録
if event_code in ['各務原', '岐阜市', '養老ロゲ', '郡上', '大垣2', 'test下呂']:
skip_date_ranges[event_code].append(gps_date)
skip_count += 1
skip_stats[event_code] += 1
skip_reasons["期間外"] += 1
continue
# チェックイン記録挿入
target_cursor.execute("""
INSERT INTO rog_gpscheckin (
zekken, event_code, cp_number, checkin_time, record_time, serial_number
) VALUES (%s, %s, %s, %s, %s, %s)
""", (zekken, event_code, cp_number, jst_create_at, jst_create_at, str(serial_number)))
event_stats[event_code].add(zekken)
success_count += 1
if success_count % 100 == 0:
print(f"GPS移行進捗: {success_count}件完了")
except Exception as e:
print(f"GPS移行エラー (Serial: {serial_number}): {e}")
error_count += 1
skip_stats[event_code] += 1
skip_reasons["その他エラー"] += 1
# コミット
rogdb_conn.commit()
print(f"\n✅ GPS移行完了:")
print(f" 成功: {success_count}")
print(f" スキップ: {skip_count}")
print(f" エラー: {error_count}")
# イベント別統計を表示
print("\n=== イベント別GPS統計 ===")
for event_code, zekken_set in event_stats.items():
print(f" {event_code}: {len(zekken_set)}チーム")
# スキップ統計を表示
print("\n=== スキップ統計(イベント別) ===")
for event_code, skip_count_by_event in skip_stats.items():
print(f" {event_code}: {skip_count_by_event}件スキップ")
# スキップ理由別統計を表示
print("\n=== スキップ理由別統計 ===")
for reason, count in skip_reasons.items():
print(f" {reason}: {count}")
# 大量スキップイベントの詳細分析結果
if large_skip_events:
print("\n=== 600件超大量スキップイベント分析結果 ===")
for event_code in large_skip_events:
total_skipped = skip_stats[event_code]
event_info = event_dates_cache.get(event_code, {})
# スキップされたGPS日付の範囲を分析
skipped_dates = skip_date_ranges.get(event_code, [])
if skipped_dates:
# 日付を昇順にソートしてユニーク化
unique_dates = sorted(set(skipped_dates))
date_range_start = unique_dates[0] if unique_dates else 'N/A'
date_range_end = unique_dates[-1] if unique_dates else 'N/A'
# 年月日の分析
year_counts = defaultdict(int)
month_counts = defaultdict(int)
for date_str in unique_dates:
try:
year = date_str[:4]
month = date_str[:7] # YYYY-MM
year_counts[year] += 1
month_counts[month] += 1
except:
pass
print(f"📊 {event_code}:")
print(f" 総スキップ数: {total_skipped}")
print(f" 設定イベント期間: {event_info.get('start_date', 'N/A')} - {event_info.get('end_date', 'N/A')}")
if skipped_dates:
print(f" スキップされたGPS記録の期間: {date_range_start} ~ {date_range_end}")
print(f" ユニークな日付数: {len(unique_dates)}")
# 年別集計
if year_counts:
print(f" 年別GPS記録数:")
for year in sorted(year_counts.keys()):
print(f" {year}年: {year_counts[year]}日分の記録")
# 月別集計上位5件
if month_counts:
top_months = sorted(month_counts.items(), key=lambda x: x[1], reverse=True)[:5]
print(f" 月別GPS記録数上位5件:")
for month, count in top_months:
print(f" {month}: {count}日分の記録")
print(f" 推測される問題: イベント期間設定が実際のGPS記録日付と大幅にずれている")
print(f" 解決策: event_tableのevent_day/end_dayを実際のイベント開催日に修正する必要があります")
print()
# 最終統計
target_cursor.execute("SELECT COUNT(*) FROM rog_gpscheckin")
total_gps_records = target_cursor.fetchone()[0]
print(f"\n最終GPS記録数: {total_gps_records}")
gifuroge_conn.close()
rogdb_conn.close()
return success_count > 0
except Exception as e:
print(f"❌ GPS移行エラー: {e}")
import traceback
traceback.print_exc()
return False
try:
# old_rogdbに直接接続
old_conn = psycopg2.connect(
host='postgres-db',
database='old_rogdb',
user='admin',
password='admin123456'
)
print("✅ old_rogdbに接続成功")
with old_conn.cursor() as old_cursor:
# === STEP 0: 移行対象イベントの確認 ===
print("\n=== STEP 0: 移行対象イベントの確認 ===")
# 新DBのイベント一覧を取得
existing_events = list(NewEvent2.objects.values_list('id', 'event_name'))
existing_event_ids = [event_id for event_id, _ in existing_events]
print(f"新DB既存イベント: {len(existing_events)}")
for event_id, event_name in existing_events[:10]:
print(f" Event {event_id}: {event_name}")
# old_rogdbでエントリーがあるイベントを確認
old_cursor.execute("""
SELECT e.id, e.event_name, COUNT(re.id) as entry_count
FROM rog_newevent2 e
LEFT JOIN rog_entry re ON e.id = re.event_id
WHERE e.id IN ({})
GROUP BY e.id, e.event_name
HAVING COUNT(re.id) > 0
ORDER BY COUNT(re.id) DESC;
""".format(','.join(map(str, existing_event_ids))))
events_with_entries = old_cursor.fetchall()
print(f"\n移行対象イベント(エントリーあり): {len(events_with_entries)}")
for event_id, event_name, entry_count in events_with_entries:
print(f" Event {event_id}: '{event_name}' - {entry_count}件のエントリー")
# === STEP 1: 全イベントのTeam & Member データ取得 ===
print("\n=== STEP 1: 全イベントの Team & Member データ取得 ===")
# 全イベントのチーム情報を取得
old_cursor.execute("""
SELECT DISTINCT rt.id, rt.team_name, rt.owner_id, rt.category_id,
rc.category_name, cu.email, cu.firstname, cu.lastname, re.event_id
FROM rog_entry re
JOIN rog_team rt ON re.team_id = rt.id
LEFT JOIN rog_newcategory rc ON rt.category_id = rc.id
LEFT JOIN rog_customuser cu ON rt.owner_id = cu.id
WHERE re.event_id IN ({})
ORDER BY re.event_id, rt.id;
""".format(','.join(map(str, existing_event_ids))))
all_team_data = old_cursor.fetchall()
print(f"全イベント関連チーム: {len(all_team_data)}")
# イベント別チーム数統計
teams_by_event = defaultdict(int)
for _, _, _, _, _, _, _, _, event_id in all_team_data:
teams_by_event[event_id] += 1
print("\nイベント別チーム数:")
for event_id, count in sorted(teams_by_event.items()):
event_name = next((name for eid, name in existing_events if eid == event_id), "不明")
print(f" Event {event_id} ({event_name}): {count}チーム")
# 全イベントのメンバー情報を取得
old_cursor.execute("""
SELECT rm.team_id, rm.user_id, cu.email, cu.firstname, cu.lastname, re.event_id
FROM rog_entry re
JOIN rog_member rm ON re.team_id = rm.team_id
JOIN rog_customuser cu ON rm.user_id = cu.id
WHERE re.event_id IN ({})
ORDER BY re.event_id, rm.team_id, rm.user_id;
""".format(','.join(map(str, existing_event_ids))))
all_member_data = old_cursor.fetchall()
print(f"全イベント関連メンバー: {len(all_member_data)}")
# === STEP 2: ユーザー移行 ===
print("\n=== STEP 2: ユーザー移行 ===")
# 関連するすべてのユーザーを取得
all_user_ids = set()
for _, _, owner_id, _, _, _, _, _, _ in all_team_data:
if owner_id:
all_user_ids.add(owner_id)
for _, user_id, _, _, _, _ in all_member_data:
all_user_ids.add(user_id)
if all_user_ids:
# 大量のユーザーIDに対応するため、バッチで処理
user_batches = [list(all_user_ids)[i:i+100] for i in range(0, len(all_user_ids), 100)]
all_user_data = []
for batch in user_batches:
old_cursor.execute(f"""
SELECT id, email, firstname, lastname, date_joined
FROM rog_customuser
WHERE id IN ({','.join(map(str, batch))})
""")
all_user_data.extend(old_cursor.fetchall())
print(f"移行対象ユーザー: {len(all_user_data)}")
migrated_users = 0
for user_id, email, first_name, last_name, date_joined in all_user_data:
user, created = CustomUser.objects.get_or_create(
id=user_id,
defaults={
'email': email or f'user{user_id}@example.com',
'first_name': first_name or '',
'last_name': last_name or '',
'username': email or f'user{user_id}',
'date_joined': date_joined,
'is_active': True
}
)
if created:
migrated_users += 1
if migrated_users <= 10: # 最初の10件のみ表示
print(f" ユーザー作成: {email} ({first_name} {last_name})")
print(f"✅ ユーザー移行完了: {migrated_users}件作成")
# === STEP 3: カテゴリ移行 ===
print("\n=== STEP 3: カテゴリ移行 ===")
migrated_categories = 0
unique_categories = set()
for _, _, _, cat_id, cat_name, _, _, _, _ in all_team_data:
if cat_id and cat_name:
unique_categories.add((cat_id, cat_name))
for cat_id, cat_name in unique_categories:
category, created = NewCategory.objects.get_or_create(
id=cat_id,
defaults={
'category_name': cat_name,
'category_number': cat_id
}
)
if created:
migrated_categories += 1
print(f" カテゴリ作成: {cat_name}")
print(f"✅ カテゴリ移行完了: {migrated_categories}件作成")
# === STEP 4: イベント別チーム移行 ===
print("\n=== STEP 4: イベント別チーム移行 ===")
total_migrated_teams = 0
for event_id, event_name in existing_events:
if event_id not in teams_by_event:
continue
print(f"\n--- Event {event_id}: {event_name} ---")
event_teams = [data for data in all_team_data if data[8] == event_id]
event_migrated_teams = 0
for team_id, team_name, owner_id, cat_id, cat_name, email, first_name, last_name, _ in event_teams:
try:
# カテゴリを取得
category = NewCategory.objects.get(id=cat_id) if cat_id else None
# チームを作成
team, created = Team.objects.get_or_create(
id=team_id,
defaults={
'team_name': team_name,
'owner_id': owner_id or 1,
'category': category,
'event_id': event_id
}
)
if created:
event_migrated_teams += 1
total_migrated_teams += 1
if event_migrated_teams <= 3: # イベントごとに最初の3件のみ表示
print(f" チーム作成: {team_name} (ID: {team_id})")
except Exception as e:
print(f" ❌ チーム作成エラー: {team_name} - {e}")
print(f"{event_name}: {event_migrated_teams}件のチームを移行")
print(f"\n✅ 全チーム移行完了: {total_migrated_teams}件作成")
# === STEP 5: メンバー移行 ===
print("\n=== STEP 5: メンバー移行 ===")
total_migrated_members = 0
for event_id, event_name in existing_events:
if event_id not in teams_by_event:
continue
event_members = [data for data in all_member_data if data[5] == event_id]
if not event_members:
continue
print(f"\n--- Event {event_id}: {event_name} ---")
event_migrated_members = 0
for team_id, user_id, email, first_name, last_name, _ in event_members:
try:
# チームとユーザーを取得
team = Team.objects.get(id=team_id)
user = CustomUser.objects.get(id=user_id)
# メンバーを作成
member, created = Member.objects.get_or_create(
team=team,
user=user
)
if created:
event_migrated_members += 1
total_migrated_members += 1
if event_migrated_members <= 3: # イベントごとに最初の3件のみ表示
print(f" メンバー追加: {email}{team.team_name}")
except Team.DoesNotExist:
print(f" ⚠️ チーム{team_id}が見つかりません")
except CustomUser.DoesNotExist:
print(f" ⚠️ ユーザー{user_id}が見つかりません")
except Exception as e:
print(f" ❌ メンバー追加エラー: {e}")
print(f"{event_name}: {event_migrated_members}件のメンバーを移行")
print(f"\n✅ 全メンバー移行完了: {total_migrated_members}件作成")
# === STEP 6: エントリー移行 ===
print("\n=== STEP 6: エントリー移行 ===")
# データベースのis_trialフィールドにデフォルト値を設定
print("データベーステーブルのis_trialフィールドを修正中...")
from django.db import connection as django_conn
with django_conn.cursor() as django_cursor:
try:
django_cursor.execute("""
ALTER TABLE rog_entry
ALTER COLUMN is_trial SET DEFAULT FALSE;
""")
print(" ✅ is_trialフィールドにデフォルト値を設定")
except Exception as e:
print(f" ⚠️ is_trial修正エラー: {e}")
total_migrated_entries = 0
for event_id, event_name in existing_events:
if event_id not in teams_by_event:
continue
print(f"\n--- Event {event_id}: {event_name} ---")
# イベント別エントリーデータを取得
old_cursor.execute("""
SELECT re.id, re.team_id, re.zekken_number, re.zekken_label,
rt.team_name, re.category_id, re.date, re.owner_id,
rc.category_name
FROM rog_entry re
JOIN rog_team rt ON re.team_id = rt.id
LEFT JOIN rog_newcategory rc ON re.category_id = rc.id
WHERE re.event_id = %s
ORDER BY re.zekken_number;
""", [event_id])
event_entry_data = old_cursor.fetchall()
event_migrated_entries = 0
for entry_id, team_id, zekken, label, team_name, cat_id, date, owner_id, cat_name in event_entry_data:
try:
# チームとカテゴリを取得
team = Team.objects.get(id=team_id)
category = NewCategory.objects.get(id=cat_id) if cat_id else None
event_obj = NewEvent2.objects.get(id=event_id)
# 既存のエントリーをチェック
existing_entry = Entry.objects.filter(team=team, event=event_obj).first()
if existing_entry:
continue
# SQLで直接エントリーを挿入
with django_conn.cursor() as django_cursor:
django_cursor.execute("""
INSERT INTO rog_entry
(date, category_id, event_id, owner_id, team_id, is_active,
zekken_number, "hasGoaled", "hasParticipated", zekken_label,
is_trial, staff_privileges, can_access_private_events, team_validation_status)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
""", [
event_obj.start_datetime, # date
cat_id, # category_id
event_id, # event_id
owner_id or 1, # owner_id
team_id, # team_id
True, # is_active
int(zekken) if zekken else 0, # zekken_number
False, # hasGoaled
False, # hasParticipated
label or f"{event_name}-{zekken}", # zekken_label
False, # is_trial
False, # staff_privileges
False, # can_access_private_events
'approved' # team_validation_status
])
event_migrated_entries += 1
total_migrated_entries += 1
if event_migrated_entries <= 3: # イベントごとに最初の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}件作成")
old_conn.close()
# === STEP 7: GPS情報移行 ===
print("\n=== STEP 7: GPS情報移行 ===")
gps_migration_success = migrate_gps_data()
if gps_migration_success:
print("✅ GPS情報移行が正常に完了しました")
else:
print("⚠️ GPS情報移行中にエラーが発生しました")
# === 最終確認 ===
print("\n=== 移行結果確認 ===")
total_teams = Team.objects.count()
total_members = Member.objects.count()
total_entries = Entry.objects.count()
print(f"総チーム数: {total_teams}")
print(f"総メンバー数: {total_members}")
print(f"総エントリー数: {total_entries}")
# GPS記録数も追加で確認
from django.db import connection as django_conn
with django_conn.cursor() as cursor:
cursor.execute("SELECT COUNT(*) FROM rog_gpscheckin")
gps_count = cursor.fetchone()[0]
print(f"総GPS記録数: {gps_count}")
# イベント別エントリー統計
print("\n=== イベント別エントリー統計 ===")
for event_id, event_name in existing_events[:10]: # 最初の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()