almost finish migrate new circumstances

This commit is contained in:
2025-08-24 19:44:36 +09:00
parent 1ba305641e
commit fe5a044c82
67 changed files with 1194889 additions and 467 deletions

314
preview_path_conversion.py Normal file
View File

@ -0,0 +1,314 @@
#!/usr/bin/env python
"""
パス変換プレビュースクリプト
実際の更新を行わずに、パス変換の結果をプレビューします。
"""
import os
import sys
import django
from pathlib import Path
import json
from datetime import datetime
# Django settings setup
BASE_DIR = Path(__file__).resolve().parent
sys.path.append(str(BASE_DIR))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()
from django.conf import settings
from rog.models import GoalImages, CheckinImages
import logging
# ロギング設定
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class PathConversionPreview:
"""パス変換プレビューサービス"""
def __init__(self):
self.s3_bucket = settings.AWS_STORAGE_BUCKET_NAME
self.s3_region = settings.AWS_S3_REGION_NAME
self.s3_base_url = f"https://{self.s3_bucket}.s3.{self.s3_region}.amazonaws.com"
def convert_local_path_to_s3_url(self, local_path, event_code, team_name, image_type='checkin'):
"""ローカルパスをS3 URLに変換プレビュー用、100文字制限対応"""
try:
filename = os.path.basename(local_path)
if image_type == 'goal' or local_path.startswith('goals/'):
s3_path = f"s3://{self.s3_bucket}/{event_code}/goals/{team_name}/{filename}"
else:
s3_path = f"s3://{self.s3_bucket}/{event_code}/{team_name}/{filename}"
# 100文字制限チェック
if len(s3_path) > 100:
# 短縮版: ファイル名のみを使用
if image_type == 'goal' or local_path.startswith('goals/'):
s3_path = f"s3://{self.s3_bucket}/goals/{filename}"
else:
s3_path = f"s3://{self.s3_bucket}/checkin/{filename}"
# それでも長い場合はファイル名だけ
if len(s3_path) > 100:
s3_path = f"s3://{self.s3_bucket}/{filename}"
return s3_path
except Exception as e:
return f"ERROR: {str(e)}"
def is_already_s3_url(self, path):
"""既にS3 URLかどうかを判定"""
return (
path and (
path.startswith('https://') or
path.startswith('http://') or
path.startswith('s3://') or
's3' in path.lower() or
'amazonaws' in path.lower()
)
)
def preview_goal_images(self, limit=10):
"""GoalImagesのパス変換をプレビュー"""
print("\n=== GoalImages パス変換プレビュー ===")
goal_images = GoalImages.objects.filter(goalimage__isnull=False).exclude(goalimage='')
total_count = goal_images.count()
print(f"総対象件数: {total_count:,}")
print(f"プレビュー件数: {min(limit, total_count)}\n")
already_s3_count = 0
conversion_examples = []
for i, goal_img in enumerate(goal_images[:limit]):
original_path = str(goal_img.goalimage)
if self.is_already_s3_url(original_path):
already_s3_count += 1
status = "🔗 既にS3 URL"
converted_path = original_path
else:
status = "🔄 変換対象"
converted_path = self.convert_local_path_to_s3_url(
original_path,
goal_img.event_code,
goal_img.team_name,
'goal'
)
conversion_examples.append({
'id': goal_img.id,
'event_code': goal_img.event_code,
'team_name': goal_img.team_name,
'cp_number': goal_img.cp_number,
'original_path': original_path,
'converted_path': converted_path,
'status': status
})
print(f"{i+1:2d}. ID={goal_img.id} {status}")
print(f" イベント: {goal_img.event_code} | チーム: {goal_img.team_name}")
print(f" 元: {original_path}")
print(f" → : {converted_path}")
print()
return {
'total_count': total_count,
'already_s3_count': already_s3_count,
'conversion_examples': conversion_examples
}
def preview_checkin_images(self, limit=10):
"""CheckinImagesのパス変換をプレビュー"""
print("\n=== CheckinImages パス変換プレビュー ===")
checkin_images = CheckinImages.objects.filter(checkinimage__isnull=False).exclude(checkinimage='')
total_count = checkin_images.count()
print(f"総対象件数: {total_count:,}")
print(f"プレビュー件数: {min(limit, total_count)}\n")
already_s3_count = 0
conversion_examples = []
for i, checkin_img in enumerate(checkin_images[:limit]):
original_path = str(checkin_img.checkinimage)
if self.is_already_s3_url(original_path):
already_s3_count += 1
status = "🔗 既にS3 URL"
converted_path = original_path
else:
status = "🔄 変換対象"
converted_path = self.convert_local_path_to_s3_url(
original_path,
checkin_img.event_code,
checkin_img.team_name,
'checkin'
)
conversion_examples.append({
'id': checkin_img.id,
'event_code': checkin_img.event_code,
'team_name': checkin_img.team_name,
'cp_number': checkin_img.cp_number,
'original_path': original_path,
'converted_path': converted_path,
'status': status
})
print(f"{i+1:2d}. ID={checkin_img.id} {status}")
print(f" イベント: {checkin_img.event_code} | チーム: {checkin_img.team_name}")
print(f" 元: {original_path}")
print(f" → : {converted_path}")
print()
return {
'total_count': total_count,
'already_s3_count': already_s3_count,
'conversion_examples': conversion_examples
}
def analyze_path_patterns(self):
"""パスパターンを分析"""
print("\n=== パスパターン分析 ===")
# GoalImagesのパターン分析
goal_paths = GoalImages.objects.filter(goalimage__isnull=False).exclude(goalimage='').values_list('goalimage', flat=True)
goal_patterns = {}
for path in goal_paths:
path_str = str(path)
if self.is_already_s3_url(path_str):
pattern = "S3_URL"
elif path_str.startswith('goals/'):
pattern = "goals/YYMMDD/filename"
elif '/' in path_str:
parts = path_str.split('/')
pattern = f"{parts[0]}/..."
else:
pattern = "filename_only"
goal_patterns[pattern] = goal_patterns.get(pattern, 0) + 1
# CheckinImagesのパターン分析
checkin_paths = CheckinImages.objects.filter(checkinimage__isnull=False).exclude(checkinimage='').values_list('checkinimage', flat=True)
checkin_patterns = {}
for path in checkin_paths:
path_str = str(path)
if self.is_already_s3_url(path_str):
pattern = "S3_URL"
elif path_str.startswith('checkin/'):
pattern = "checkin/YYMMDD/filename"
elif '/' in path_str:
parts = path_str.split('/')
pattern = f"{parts[0]}/..."
else:
pattern = "filename_only"
checkin_patterns[pattern] = checkin_patterns.get(pattern, 0) + 1
print("GoalImages パスパターン:")
for pattern, count in sorted(goal_patterns.items(), key=lambda x: x[1], reverse=True):
print(f" {pattern}: {count:,}")
print("\nCheckinImages パスパターン:")
for pattern, count in sorted(checkin_patterns.items(), key=lambda x: x[1], reverse=True):
print(f" {pattern}: {count:,}")
return {
'goal_patterns': goal_patterns,
'checkin_patterns': checkin_patterns
}
def generate_preview_report(self, goal_preview, checkin_preview, patterns):
"""プレビューレポートを生成"""
report = {
'preview_timestamp': datetime.now().isoformat(),
's3_configuration': {
'bucket': self.s3_bucket,
'region': self.s3_region,
'base_url': self.s3_base_url
},
'goal_images': goal_preview,
'checkin_images': checkin_preview,
'path_patterns': patterns,
'summary': {
'total_goal_images': goal_preview['total_count'],
'total_checkin_images': checkin_preview['total_count'],
'total_images': goal_preview['total_count'] + checkin_preview['total_count'],
'already_s3_urls': goal_preview['already_s3_count'] + checkin_preview['already_s3_count'],
'requires_conversion': (goal_preview['total_count'] - goal_preview['already_s3_count']) +
(checkin_preview['total_count'] - checkin_preview['already_s3_count'])
}
}
# レポートファイルの保存
report_file = f'path_conversion_preview_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
with open(report_file, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
# サマリー表示
print("\n" + "="*60)
print("📊 パス変換プレビューサマリー")
print("="*60)
print(f"🎯 総画像数: {report['summary']['total_images']:,}")
print(f" - ゴール画像: {report['summary']['total_goal_images']:,}")
print(f" - チェックイン画像: {report['summary']['total_checkin_images']:,}")
print(f"🔗 既にS3 URL: {report['summary']['already_s3_urls']:,}")
print(f"🔄 変換必要: {report['summary']['requires_conversion']:,}")
print(f"📄 詳細レポート: {report_file}")
return report
def main():
"""メイン関数"""
print("="*60)
print("👀 データベースパス変換プレビューツール")
print("="*60)
print("このツールは以下を実行します:")
print("1. パス変換のプレビュー表示")
print("2. パスパターンの分析")
print("3. 変換レポートの生成")
print()
print("⚠️ 注意: データベースの実際の更新は行いません")
print()
# プレビュー件数の設定
try:
limit_input = input("プレビュー表示件数を入力してください [デフォルト: 10]: ").strip()
limit = int(limit_input) if limit_input else 10
if limit <= 0:
limit = 10
except ValueError:
limit = 10
print(f"\n🔍 パス変換をプレビューします(各タイプ{limit}件まで表示)...\n")
# プレビュー実行
preview_service = PathConversionPreview()
# 1. パスパターン分析
patterns = preview_service.analyze_path_patterns()
# 2. GoalImagesプレビュー
goal_preview = preview_service.preview_goal_images(limit)
# 3. CheckinImagesプレビュー
checkin_preview = preview_service.preview_checkin_images(limit)
# 4. レポート生成
report = preview_service.generate_preview_report(goal_preview, checkin_preview, patterns)
print("\n✅ プレビューが完了しました!")
print("実際の変換を実行する場合は update_image_paths_to_s3.py を使用してください。")
if __name__ == "__main__":
main()