add migrate_old_team/entry/goalimages_enhanced

This commit is contained in:
2025-08-25 19:48:33 +09:00
parent 8e3f7024a2
commit 37ee26c3fd
6 changed files with 1394 additions and 0 deletions

View File

@ -0,0 +1,352 @@
#!/usr/bin/env python3
"""
rog_team テーブル専用移行スクリプト (構造変換対応)
old_rogdb の rog_team から rogdb の rog_team へ構造変換を行いながらデータ移行
"""
import os
import sys
import psycopg2
from datetime import datetime, timezone
import logging
# ログ設定
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# データベース設定
OLD_ROGDB_CONFIG = {
'host': os.getenv('OLD_ROGDB_HOST', 'postgres-db'),
'database': os.getenv('OLD_ROGDB_NAME', 'old_rogdb'),
'user': os.getenv('OLD_ROGDB_USER', 'admin'),
'password': os.getenv('OLD_ROGDB_PASSWORD', 'admin123456'),
'port': int(os.getenv('OLD_ROGDB_PORT', 5432))
}
ROGDB_CONFIG = {
'host': os.getenv('ROGDB_HOST', 'postgres-db'),
'database': os.getenv('ROGDB_NAME', 'rogdb'),
'user': os.getenv('ROGDB_USER', 'admin'),
'password': os.getenv('ROGDB_PASSWORD', 'admin123456'),
'port': int(os.getenv('ROGDB_PORT', 5432))
}
class RogTeamMigrator:
"""rog_team テーブル専用移行クラス"""
def __init__(self):
self.old_conn = None
self.new_conn = None
self.old_cursor = None
self.new_cursor = None
self.default_event_id = None
def connect_databases(self):
"""データベース接続"""
try:
logger.info("データベースに接続中...")
self.old_conn = psycopg2.connect(**OLD_ROGDB_CONFIG)
self.new_conn = psycopg2.connect(**ROGDB_CONFIG)
self.old_conn.autocommit = True
self.new_conn.autocommit = False
self.old_cursor = self.old_conn.cursor()
self.new_cursor = self.new_conn.cursor()
logger.info("✅ データベース接続成功")
return True
except Exception as e:
logger.error(f"❌ データベース接続エラー: {e}")
return False
def close_connections(self):
"""データベース接続クローズ"""
try:
if self.old_cursor:
self.old_cursor.close()
if self.new_cursor:
self.new_cursor.close()
if self.old_conn:
self.old_conn.close()
if self.new_conn:
self.new_conn.close()
logger.info("データベース接続をクローズしました")
except Exception as e:
logger.warning(f"接続クローズ時の警告: {e}")
def get_default_event_id(self):
"""デフォルトのevent_idを取得または作成"""
try:
# 既存のイベントを探す
self.new_cursor.execute("""
SELECT id FROM rog_newevent2
WHERE event_name LIKE '%移行%' OR event_name LIKE '%default%'
ORDER BY id LIMIT 1
""")
result = self.new_cursor.fetchone()
if result:
event_id = result[0]
logger.info(f"既存のデフォルトイベントを使用: event_id = {event_id}")
return event_id
# なければ最初のイベントを使用
self.new_cursor.execute("""
SELECT id FROM rog_newevent2
ORDER BY id LIMIT 1
""")
result = self.new_cursor.fetchone()
if result:
event_id = result[0]
logger.info(f"最初のイベントをデフォルトとして使用: event_id = {event_id}")
return event_id
# イベントがない場合はエラー
logger.error("❌ rog_newevent2 テーブルにイベントが存在しません")
return None
except Exception as e:
logger.error(f"❌ デフォルトevent_id取得エラー: {e}")
return None
def get_category_mapping(self):
"""カテゴリIDのマッピングを確認"""
try:
# 旧DBのカテゴリ
self.old_cursor.execute("SELECT id, category_name FROM rog_newcategory ORDER BY id")
old_categories = dict(self.old_cursor.fetchall())
# 新DBのカテゴリ
self.new_cursor.execute("SELECT id, category_name FROM rog_newcategory ORDER BY id")
new_categories = dict(self.new_cursor.fetchall())
logger.info(f"旧DB カテゴリ: {old_categories}")
logger.info(f"新DB カテゴリ: {new_categories}")
# 名前ベースでマッピング作成
category_mapping = {}
for old_id, old_name in old_categories.items():
for new_id, new_name in new_categories.items():
if old_name == new_name:
category_mapping[old_id] = new_id
break
else:
# マッチしない場合は最初のカテゴリを使用
if new_categories:
category_mapping[old_id] = min(new_categories.keys())
logger.warning(f"カテゴリマッピング失敗 - デフォルト使用: {old_id} -> {category_mapping[old_id]}")
return category_mapping
except Exception as e:
logger.error(f"❌ カテゴリマッピング取得エラー: {e}")
return {}
def convert_team_record(self, old_record, category_mapping):
"""旧レコードを新レコード形式に変換"""
old_id, old_team_name, old_category_id, old_owner_id = old_record
# 新しいレコード作成
new_record = {
'id': old_id,
'team_name': old_team_name,
'category_id': category_mapping.get(old_category_id, old_category_id),
'owner_id': old_owner_id,
# 新しいフィールドにデフォルト値を設定
'class_name': '', # 空文字列
'event_id': self.default_event_id, # デフォルトイベント
'location': None, # PostGIS座標は後で設定可能
'password': '', # パスワードなし
'trial': False, # 本番チーム
'zekken_number': '', # ゼッケン番号なし
'created_at': datetime.now(timezone.utc),
'updated_at': datetime.now(timezone.utc)
}
return new_record
def migrate_rog_team(self):
"""rog_team テーブルのデータ移行"""
logger.info("=" * 60)
logger.info("rog_team テーブル構造変換移行開始")
logger.info("=" * 60)
try:
# デフォルトevent_id取得
self.default_event_id = self.get_default_event_id()
if not self.default_event_id:
return False
# カテゴリマッピング取得
category_mapping = self.get_category_mapping()
# 旧データ取得
logger.info("旧rog_teamデータを取得中...")
self.old_cursor.execute("""
SELECT id, team_name, category_id, owner_id
FROM rog_team
ORDER BY id
""")
old_records = self.old_cursor.fetchall()
if not old_records:
logger.info("✅ 移行対象データがありません")
return True
logger.info(f"移行対象レコード数: {len(old_records)}")
# 統計情報
inserted_count = 0
updated_count = 0
error_count = 0
# レコード別処理
for i, old_record in enumerate(old_records):
try:
# レコード変換
new_record = self.convert_team_record(old_record, category_mapping)
team_id = new_record['id']
# 既存レコード確認
self.new_cursor.execute(
"SELECT COUNT(*) FROM rog_team WHERE id = %s",
(team_id,)
)
exists = self.new_cursor.fetchone()[0] > 0
if exists:
# UPDATE処理
update_query = """
UPDATE rog_team SET
team_name = %s,
category_id = %s,
owner_id = %s,
class_name = %s,
event_id = %s,
location = %s,
password = %s,
trial = %s,
zekken_number = %s,
updated_at = %s
WHERE id = %s
"""
self.new_cursor.execute(update_query, (
new_record['team_name'],
new_record['category_id'],
new_record['owner_id'],
new_record['class_name'],
new_record['event_id'],
new_record['location'],
new_record['password'],
new_record['trial'],
new_record['zekken_number'],
new_record['updated_at'],
team_id
))
updated_count += 1
else:
# INSERT処理
insert_query = """
INSERT INTO rog_team (
id, team_name, category_id, owner_id,
class_name, event_id, location, password,
trial, zekken_number, created_at, updated_at
) VALUES (
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
)
"""
self.new_cursor.execute(insert_query, (
new_record['id'],
new_record['team_name'],
new_record['category_id'],
new_record['owner_id'],
new_record['class_name'],
new_record['event_id'],
new_record['location'],
new_record['password'],
new_record['trial'],
new_record['zekken_number'],
new_record['created_at'],
new_record['updated_at']
))
inserted_count += 1
# 進捗表示
if (i + 1) % 50 == 0:
self.new_conn.commit()
logger.info(f" 進捗: {i + 1}/{len(old_records)} 件処理完了")
except Exception as e:
error_count += 1
logger.error(f" レコード処理エラー (ID: {old_record[0]}): {e}")
# トランザクションロールバック
try:
self.new_conn.rollback()
except:
pass
if error_count > 10:
logger.error("❌ エラー数が上限を超えました")
break
# 最終コミット
self.new_conn.commit()
# 結果サマリー
logger.info("=" * 60)
logger.info("rog_team 移行完了")
logger.info("=" * 60)
logger.info(f"挿入: {inserted_count}")
logger.info(f"更新: {updated_count}")
logger.info(f"エラー: {error_count}")
logger.info(f"総処理: {len(old_records)}")
if error_count == 0:
logger.info("✅ rog_team移行が正常に完了しました")
return True
else:
logger.warning(f"⚠️ {error_count}件のエラーがありました")
return False
except Exception as e:
logger.error(f"❌ rog_team移行エラー: {e}")
try:
self.new_conn.rollback()
except:
pass
return False
def run(self):
"""移行実行"""
try:
if not self.connect_databases():
return False
return self.migrate_rog_team()
finally:
self.close_connections()
def main():
"""メイン処理"""
logger.info("rog_team テーブル構造変換移行スクリプト")
migrator = RogTeamMigrator()
success = migrator.run()
if success:
logger.info("🎉 移行が正常に完了しました!")
else:
logger.error("💥 移行中にエラーが発生しました")
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()