almost finish migrate new circumstances
This commit is contained in:
314
preview_path_conversion.py
Normal file
314
preview_path_conversion.py
Normal 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()
|
||||
Reference in New Issue
Block a user