add location migrate
This commit is contained in:
120
migrate_location_to_location2025_complete.py
Normal file
120
migrate_location_to_location2025_complete.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
LocationからLocation2025への完全データ移行スクリプト
|
||||||
|
|
||||||
|
条件:
|
||||||
|
- NewEvent2ごとにlocation.groupにそのevent_codeが含まれているものを抽出
|
||||||
|
- location.cpをlocation2025.cp_numberに変換
|
||||||
|
- location2025.event_idにはnewevent2.idを代入
|
||||||
|
|
||||||
|
実行前にlocation2025のデータを削除してから実行
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rog.models import Location, Location2025, NewEvent2
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=== Location から Location2025 への完全データ移行 ===")
|
||||||
|
|
||||||
|
# 1. Location2025の既存データを削除
|
||||||
|
print("\n1. Location2025の既存データを削除中...")
|
||||||
|
deleted_count = Location2025.objects.count()
|
||||||
|
Location2025.objects.all().delete()
|
||||||
|
print(f" 削除済み: {deleted_count}件")
|
||||||
|
|
||||||
|
# 2. NewEvent2のevent_codeマップを作成
|
||||||
|
print("\n2. NewEvent2のevent_codeマップを作成中...")
|
||||||
|
|
||||||
|
events = NewEvent2.objects.filter(event_code__isnull=False).exclude(event_code='')
|
||||||
|
event_code_map = {}
|
||||||
|
for event in events:
|
||||||
|
event_code_map[event.event_code] = event
|
||||||
|
print(f" Event_code: '{event.event_code}' -> ID: {event.id} ({event.event_name})")
|
||||||
|
|
||||||
|
print(f" 有効なevent_code数: {len(event_code_map)}件")
|
||||||
|
|
||||||
|
# 3. 全Locationを取得
|
||||||
|
print("\n3. 移行対象のLocationレコードを取得中...")
|
||||||
|
locations = Location.objects.all()
|
||||||
|
print(f" 総Location数: {locations.count()}件")
|
||||||
|
|
||||||
|
# 4. 条件に合致するLocationを移行
|
||||||
|
print("\n4. データ移行中...")
|
||||||
|
|
||||||
|
migrated_count = 0
|
||||||
|
skipped_count = 0
|
||||||
|
error_count = 0
|
||||||
|
|
||||||
|
for location in locations:
|
||||||
|
try:
|
||||||
|
# groupが空の場合はスキップ
|
||||||
|
if not location.group:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# location.groupに含まれるevent_codeを検索
|
||||||
|
matched_event = None
|
||||||
|
matched_event_code = None
|
||||||
|
|
||||||
|
for event_code, event in event_code_map.items():
|
||||||
|
if event_code in location.group:
|
||||||
|
matched_event = event
|
||||||
|
matched_event_code = event_code
|
||||||
|
break
|
||||||
|
|
||||||
|
# マッチするevent_codeがない場合はスキップ
|
||||||
|
if not matched_event:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Location2025レコードを作成
|
||||||
|
location2025 = Location2025(
|
||||||
|
cp_number=location.cp, # cpをcp_numberに代入
|
||||||
|
name=location.location_name, # location_nameを使用
|
||||||
|
description=location.address or '', # addressをdescriptionとして使用
|
||||||
|
latitude=location.latitude,
|
||||||
|
longitude=location.longitude,
|
||||||
|
point=location.checkin_point, # checkin_pointをpointとして使用
|
||||||
|
geom=location.geom,
|
||||||
|
sub_loc_id=location.sub_loc_id,
|
||||||
|
subcategory=location.subcategory,
|
||||||
|
event_id=matched_event.id, # NewEvent2のIDを設定
|
||||||
|
created_at=location.created_at,
|
||||||
|
updated_at=location.last_updated_at,
|
||||||
|
)
|
||||||
|
|
||||||
|
location2025.save()
|
||||||
|
|
||||||
|
print(f" ✅ 移行完了: {location.cp} -> {location2025.cp_number} ({location.location_name}) [Event: {matched_event_code}]")
|
||||||
|
migrated_count += 1
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ❌ エラー: {location.cp} - {str(e)}")
|
||||||
|
error_count += 1
|
||||||
|
|
||||||
|
# 5. 結果サマリー
|
||||||
|
print(f"\n=== 移行結果サマリー ===")
|
||||||
|
print(f"移行完了: {migrated_count}件")
|
||||||
|
print(f"スキップ: {skipped_count}件")
|
||||||
|
print(f"エラー: {error_count}件")
|
||||||
|
print(f"総処理: {migrated_count + skipped_count + error_count}件")
|
||||||
|
|
||||||
|
# 6. Location2025の最終件数確認
|
||||||
|
final_count = Location2025.objects.count()
|
||||||
|
print(f"\nLocation2025最終件数: {final_count}件")
|
||||||
|
|
||||||
|
# 7. event_id別の統計
|
||||||
|
print(f"\n=== event_id別統計 ===")
|
||||||
|
for event_code, event in event_code_map.items():
|
||||||
|
count = Location2025.objects.filter(event_id=event.id).count()
|
||||||
|
print(f" Event '{event_code}' (ID: {event.id}): {count}件")
|
||||||
|
|
||||||
|
if migrated_count > 0:
|
||||||
|
print("\n✅ データ移行が正常に完了しました")
|
||||||
|
else:
|
||||||
|
print("\n⚠️ 移行されたデータがありません")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
176
migrate_location_to_location2025_enhanced.py
Normal file
176
migrate_location_to_location2025_enhanced.py
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
LocationからLocation2025への完全データ移行スクリプト(フィールド追加版)
|
||||||
|
|
||||||
|
更新内容:
|
||||||
|
- photos, videos, remark, tags, evaluation_value, hidden_location フィールドを追加
|
||||||
|
- cp_pointとphoto_pointは同じもので、checkin_pointとして移行
|
||||||
|
- location.cpを直接location2025.cp_numberに書き込み
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import django
|
||||||
|
|
||||||
|
# Django設定
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from rog.models import Location, Location2025, NewEvent2
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
def main():
|
||||||
|
User = get_user_model()
|
||||||
|
default_user = User.objects.first()
|
||||||
|
|
||||||
|
print('=== Location から Location2025 への完全データ移行(フィールド追加版) ===')
|
||||||
|
|
||||||
|
# 1. Location2025の既存データを削除
|
||||||
|
print('\n1. Location2025の既存データを削除中...')
|
||||||
|
deleted_count = Location2025.objects.count()
|
||||||
|
Location2025.objects.all().delete()
|
||||||
|
print(f' 削除済み: {deleted_count}件')
|
||||||
|
|
||||||
|
# 2. NewEvent2のevent_codeマップを作成
|
||||||
|
print('\n2. NewEvent2のevent_codeマップを作成中...')
|
||||||
|
events = NewEvent2.objects.filter(event_code__isnull=False).exclude(event_code='')
|
||||||
|
event_code_map = {}
|
||||||
|
for event in events:
|
||||||
|
event_code_map[event.event_code] = event
|
||||||
|
print(f' 有効なevent_code数: {len(event_code_map)}件')
|
||||||
|
|
||||||
|
# 3. 全Locationを取得し、cp_number+event_idのユニークな組み合わせのみを処理
|
||||||
|
print('\n3. ユニークなcp_number+event_idの組み合わせで移行中...')
|
||||||
|
|
||||||
|
locations = Location.objects.all()
|
||||||
|
processed_combinations = set()
|
||||||
|
migrated_count = 0
|
||||||
|
skipped_count = 0
|
||||||
|
error_count = 0
|
||||||
|
event_stats = defaultdict(int)
|
||||||
|
|
||||||
|
for location in locations:
|
||||||
|
try:
|
||||||
|
# groupが空の場合はスキップ
|
||||||
|
if not location.group:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# location.groupに含まれるevent_codeを検索
|
||||||
|
matched_event = None
|
||||||
|
matched_event_code = None
|
||||||
|
|
||||||
|
for event_code, event in event_code_map.items():
|
||||||
|
if event_code in location.group:
|
||||||
|
matched_event = event
|
||||||
|
matched_event_code = event_code
|
||||||
|
break
|
||||||
|
|
||||||
|
# マッチするevent_codeがない場合はスキップ
|
||||||
|
if not matched_event:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# cp_number + event_idの組み合わせを確認
|
||||||
|
combination_key = (location.cp, matched_event.id)
|
||||||
|
if combination_key in processed_combinations:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# この組み合わせを処理済みとしてマーク
|
||||||
|
processed_combinations.add(combination_key)
|
||||||
|
|
||||||
|
# MultiPointからPointに変換
|
||||||
|
point_location = None
|
||||||
|
if location.geom and len(location.geom) > 0:
|
||||||
|
first_point = location.geom[0]
|
||||||
|
point_location = Point(first_point.x, first_point.y)
|
||||||
|
elif location.longitude and location.latitude:
|
||||||
|
point_location = Point(location.longitude, location.latitude)
|
||||||
|
|
||||||
|
# Location2025レコードを作成(update_or_create使用)
|
||||||
|
location2025, created = Location2025.objects.update_or_create(
|
||||||
|
cp_number=location.cp, # location.cpを直接使用
|
||||||
|
event=matched_event,
|
||||||
|
defaults={
|
||||||
|
'cp_name': location.location_name or '',
|
||||||
|
'sub_loc_id': location.sub_loc_id or '',
|
||||||
|
'subcategory': location.subcategory or '',
|
||||||
|
'latitude': location.latitude or 0.0,
|
||||||
|
'longitude': location.longitude or 0.0,
|
||||||
|
'location': point_location,
|
||||||
|
# cp_pointとphoto_pointは同じもので、checkin_pointとして移行
|
||||||
|
'cp_point': int(location.checkin_point) if location.checkin_point else 0,
|
||||||
|
'photo_point': int(location.checkin_point) if location.checkin_point else 0,
|
||||||
|
'buy_point': int(location.buy_point) if location.buy_point else 0,
|
||||||
|
'checkin_radius': location.checkin_radius or 100.0,
|
||||||
|
'auto_checkin': location.auto_checkin or False,
|
||||||
|
'shop_closed': location.shop_closed or False,
|
||||||
|
'shop_shutdown': location.shop_shutdown or False,
|
||||||
|
'opening_hours': '',
|
||||||
|
'address': location.address or '',
|
||||||
|
'phone': location.phone or '',
|
||||||
|
'website': '',
|
||||||
|
'description': location.remark or '',
|
||||||
|
# 追加フィールド
|
||||||
|
'photos': location.photos or '',
|
||||||
|
'videos': location.videos or '',
|
||||||
|
'remark': location.remark or '',
|
||||||
|
'tags': location.tags or '',
|
||||||
|
'evaluation_value': location.evaluation_value or '',
|
||||||
|
'hidden_location': location.hidden_location or False,
|
||||||
|
# 管理情報
|
||||||
|
'is_active': True,
|
||||||
|
'sort_order': 0,
|
||||||
|
'csv_source_file': 'migration_from_location',
|
||||||
|
'created_by': default_user,
|
||||||
|
'updated_by': default_user,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
migrated_count += 1
|
||||||
|
event_stats[matched_event_code] += 1
|
||||||
|
|
||||||
|
if migrated_count % 100 == 0:
|
||||||
|
print(f' 進捗: {migrated_count}件完了')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f' ❌ エラー: CP {location.cp} - {str(e)}')
|
||||||
|
error_count += 1
|
||||||
|
|
||||||
|
# 4. 結果サマリー
|
||||||
|
print(f'\n=== 移行結果サマリー ===')
|
||||||
|
print(f'移行完了: {migrated_count}件')
|
||||||
|
print(f'スキップ: {skipped_count}件')
|
||||||
|
print(f'エラー: {error_count}件')
|
||||||
|
print(f'総処理: {migrated_count + skipped_count + error_count}件')
|
||||||
|
|
||||||
|
# 5. Location2025の最終件数確認
|
||||||
|
final_count = Location2025.objects.count()
|
||||||
|
print(f'\nLocation2025最終件数: {final_count}件')
|
||||||
|
|
||||||
|
# 6. event_code別の統計
|
||||||
|
print(f'\n=== event_code別統計 ===')
|
||||||
|
for event_code, count in event_stats.items():
|
||||||
|
print(f' Event "{event_code}": {count}件')
|
||||||
|
|
||||||
|
# 7. 移行されたフィールドの確認
|
||||||
|
if migrated_count > 0:
|
||||||
|
print('\n=== 移行フィールド確認(サンプル) ===')
|
||||||
|
sample = Location2025.objects.first()
|
||||||
|
print(f' CP番号: {sample.cp_number}')
|
||||||
|
print(f' CP名: {sample.cp_name}')
|
||||||
|
print(f' CPポイント: {sample.cp_point}')
|
||||||
|
print(f' フォトポイント: {sample.photo_point}')
|
||||||
|
print(f' 写真: {sample.photos[:50]}...' if sample.photos else ' 写真: (空)')
|
||||||
|
print(f' 動画: {sample.videos[:50]}...' if sample.videos else ' 動画: (空)')
|
||||||
|
print(f' タグ: {sample.tags[:50]}...' if sample.tags else ' タグ: (空)')
|
||||||
|
print(f' 評価値: {sample.evaluation_value}')
|
||||||
|
print(f' 隠しロケーション: {sample.hidden_location}')
|
||||||
|
|
||||||
|
print('\n✅ 全フィールド対応のデータ移行が正常に完了しました')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
150
migrate_location_to_location2025_final.py
Normal file
150
migrate_location_to_location2025_final.py
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
LocationからLocation2025への完全データ移行スクリプト(最終版)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rog.models import Location, Location2025, NewEvent2
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
def main():
|
||||||
|
User = get_user_model()
|
||||||
|
default_user = User.objects.first()
|
||||||
|
|
||||||
|
print('=== Location から Location2025 への完全データ移行(最終版) ===')
|
||||||
|
|
||||||
|
# 1. Location2025の既存データを削除
|
||||||
|
print('\n1. Location2025の既存データを削除中...')
|
||||||
|
deleted_count = Location2025.objects.count()
|
||||||
|
Location2025.objects.all().delete()
|
||||||
|
print(f' 削除済み: {deleted_count}件')
|
||||||
|
|
||||||
|
# 2. NewEvent2のevent_codeマップを作成
|
||||||
|
print('\n2. NewEvent2のevent_codeマップを作成中...')
|
||||||
|
events = NewEvent2.objects.filter(event_code__isnull=False).exclude(event_code='')
|
||||||
|
event_code_map = {}
|
||||||
|
for event in events:
|
||||||
|
event_code_map[event.event_code] = event
|
||||||
|
print(f' 有効なevent_code数: {len(event_code_map)}件')
|
||||||
|
|
||||||
|
# 3. 全Locationを取得
|
||||||
|
print('\n3. 移行対象のLocationレコードを取得中...')
|
||||||
|
locations = Location.objects.all()
|
||||||
|
print(f' 総Location数: {locations.count()}件')
|
||||||
|
|
||||||
|
# 4. 条件に合致するLocationを移行
|
||||||
|
print('\n4. データ移行中...')
|
||||||
|
|
||||||
|
migrated_count = 0
|
||||||
|
skipped_count = 0
|
||||||
|
error_count = 0
|
||||||
|
cp_number_counter = {} # event_id別のcp_numberカウンター
|
||||||
|
|
||||||
|
for i, location in enumerate(locations):
|
||||||
|
try:
|
||||||
|
# 進捗表示(1000件ごと)
|
||||||
|
if i % 1000 == 0:
|
||||||
|
print(f' 処理中: {i}/{locations.count()}件')
|
||||||
|
|
||||||
|
# groupが空の場合はスキップ
|
||||||
|
if not location.group:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# location.groupに含まれるevent_codeを検索
|
||||||
|
matched_event = None
|
||||||
|
matched_event_code = None
|
||||||
|
|
||||||
|
for event_code, event in event_code_map.items():
|
||||||
|
if event_code in location.group:
|
||||||
|
matched_event = event
|
||||||
|
matched_event_code = event_code
|
||||||
|
break
|
||||||
|
|
||||||
|
# マッチするevent_codeがない場合はスキップ
|
||||||
|
if not matched_event:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# cp_numberの処理(0の場合は自動採番)
|
||||||
|
cp_number = int(location.cp) if location.cp else 0
|
||||||
|
if cp_number == 0:
|
||||||
|
# event_id別に自動採番
|
||||||
|
if matched_event.id not in cp_number_counter:
|
||||||
|
cp_number_counter[matched_event.id] = 10000 # 10000から開始
|
||||||
|
cp_number = cp_number_counter[matched_event.id]
|
||||||
|
cp_number_counter[matched_event.id] += 1
|
||||||
|
|
||||||
|
# MultiPointからPointに変換
|
||||||
|
point_location = None
|
||||||
|
if location.geom and len(location.geom) > 0:
|
||||||
|
first_point = location.geom[0]
|
||||||
|
point_location = Point(first_point.x, first_point.y)
|
||||||
|
elif location.longitude and location.latitude:
|
||||||
|
point_location = Point(location.longitude, location.latitude)
|
||||||
|
|
||||||
|
# Location2025レコードを作成
|
||||||
|
location2025 = Location2025(
|
||||||
|
cp_number=cp_number,
|
||||||
|
event=matched_event,
|
||||||
|
cp_name=location.location_name,
|
||||||
|
sub_loc_id=location.sub_loc_id or '',
|
||||||
|
subcategory=location.subcategory or '',
|
||||||
|
latitude=location.latitude or 0.0,
|
||||||
|
longitude=location.longitude or 0.0,
|
||||||
|
location=point_location,
|
||||||
|
cp_point=int(location.checkin_point) if location.checkin_point else 0,
|
||||||
|
photo_point=0,
|
||||||
|
buy_point=int(location.buy_point) if location.buy_point else 0,
|
||||||
|
checkin_radius=location.checkin_radius or 100.0,
|
||||||
|
auto_checkin=location.auto_checkin or False,
|
||||||
|
shop_closed=location.shop_closed or False,
|
||||||
|
shop_shutdown=location.shop_shutdown or False,
|
||||||
|
opening_hours='',
|
||||||
|
address=location.address or '',
|
||||||
|
phone=location.phone or '',
|
||||||
|
website='',
|
||||||
|
description=location.remark or '',
|
||||||
|
is_active=True,
|
||||||
|
sort_order=0,
|
||||||
|
csv_source_file='migration_from_location',
|
||||||
|
created_by=default_user,
|
||||||
|
updated_by=default_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
location2025.save()
|
||||||
|
migrated_count += 1
|
||||||
|
|
||||||
|
# 最初の10件は詳細ログ
|
||||||
|
if migrated_count <= 10:
|
||||||
|
print(f' ✅ 移行完了: {location.cp} -> {location2025.cp_number} ({location.location_name}) [Event: {matched_event_code}]')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f' ❌ エラー: {location.cp} - {str(e)}')
|
||||||
|
error_count += 1
|
||||||
|
|
||||||
|
# 5. 結果サマリー
|
||||||
|
print(f'\n=== 移行結果サマリー ===')
|
||||||
|
print(f'移行完了: {migrated_count}件')
|
||||||
|
print(f'スキップ: {skipped_count}件')
|
||||||
|
print(f'エラー: {error_count}件')
|
||||||
|
print(f'総処理: {migrated_count + skipped_count + error_count}件')
|
||||||
|
|
||||||
|
# 6. Location2025の最終件数確認
|
||||||
|
final_count = Location2025.objects.count()
|
||||||
|
print(f'\nLocation2025最終件数: {final_count}件')
|
||||||
|
|
||||||
|
# 7. event_id別の統計
|
||||||
|
print(f'\n=== event_id別統計 ===')
|
||||||
|
for event_code, event in event_code_map.items():
|
||||||
|
count = Location2025.objects.filter(event=event).count()
|
||||||
|
if count > 0:
|
||||||
|
print(f' Event "{event_code}" (ID: {event.id}): {count}件')
|
||||||
|
|
||||||
|
if migrated_count > 0:
|
||||||
|
print('\n✅ データ移行が正常に完了しました')
|
||||||
|
else:
|
||||||
|
print('\n⚠️ 移行されたデータがありません')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
397
migrate_location_to_location2025_with_validation.py
Normal file
397
migrate_location_to_location2025_with_validation.py
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
LocationからLocation2025への完全データ移行スクリプト(統計検証付き)
|
||||||
|
|
||||||
|
機能:
|
||||||
|
- 全フィールド対応の完全データ移行
|
||||||
|
- リアルタイム統計検証
|
||||||
|
- データ品質チェック
|
||||||
|
- 移行前後の比較
|
||||||
|
- 詳細レポート生成
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import django
|
||||||
|
from collections import defaultdict, Counter
|
||||||
|
|
||||||
|
# Django設定
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from rog.models import Location, Location2025, NewEvent2
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.gis.geos import Point
|
||||||
|
|
||||||
|
def analyze_source_data():
|
||||||
|
"""移行前のデータ分析"""
|
||||||
|
print('=== 移行前データ分析 ===')
|
||||||
|
|
||||||
|
total_locations = Location.objects.count()
|
||||||
|
print(f'総Location件数: {total_locations}件')
|
||||||
|
|
||||||
|
# グループ別統計
|
||||||
|
with_group = Location.objects.exclude(group__isnull=True).exclude(group='').count()
|
||||||
|
without_group = total_locations - with_group
|
||||||
|
print(f'groupありLocation: {with_group}件')
|
||||||
|
print(f'groupなしLocation: {without_group}件')
|
||||||
|
|
||||||
|
# 座標データ統計
|
||||||
|
with_geom = Location.objects.exclude(geom__isnull=True).count()
|
||||||
|
with_lat_lng = Location.objects.exclude(longitude__isnull=True).exclude(latitude__isnull=True).count()
|
||||||
|
print(f'geom座標あり: {with_geom}件')
|
||||||
|
print(f'lat/lng座標あり: {with_lat_lng}件')
|
||||||
|
|
||||||
|
# フィールド統計
|
||||||
|
fields_stats = {}
|
||||||
|
text_fields = ['photos', 'videos', 'remark', 'tags', 'evaluation_value', 'sub_loc_id', 'subcategory']
|
||||||
|
numeric_fields = ['checkin_point', 'buy_point']
|
||||||
|
boolean_fields = ['hidden_location']
|
||||||
|
|
||||||
|
for field in text_fields:
|
||||||
|
if hasattr(Location, field):
|
||||||
|
count = Location.objects.exclude(**{f'{field}__isnull': True}).exclude(**{field: ''}).count()
|
||||||
|
fields_stats[field] = count
|
||||||
|
print(f'{field}データあり: {count}件')
|
||||||
|
|
||||||
|
for field in numeric_fields:
|
||||||
|
if hasattr(Location, field):
|
||||||
|
count = Location.objects.exclude(**{f'{field}__isnull': True}).exclude(**{field: 0}).count()
|
||||||
|
fields_stats[field] = count
|
||||||
|
print(f'{field}データあり: {count}件')
|
||||||
|
|
||||||
|
for field in boolean_fields:
|
||||||
|
if hasattr(Location, field):
|
||||||
|
count = Location.objects.filter(**{field: True}).count()
|
||||||
|
fields_stats[field] = count
|
||||||
|
print(f'{field}データあり: {count}件')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'total': total_locations,
|
||||||
|
'with_group': with_group,
|
||||||
|
'without_group': without_group,
|
||||||
|
'with_geom': with_geom,
|
||||||
|
'with_lat_lng': with_lat_lng,
|
||||||
|
'fields': fields_stats
|
||||||
|
}
|
||||||
|
|
||||||
|
def validate_migration_data(source_stats):
|
||||||
|
"""移行後データ検証"""
|
||||||
|
print('\n=== 移行後データ検証 ===')
|
||||||
|
|
||||||
|
total_migrated = Location2025.objects.count()
|
||||||
|
print(f'移行完了件数: {total_migrated}件')
|
||||||
|
|
||||||
|
# フィールド検証
|
||||||
|
migrated_stats = {}
|
||||||
|
field_mapping = {
|
||||||
|
'photos': 'photos',
|
||||||
|
'videos': 'videos',
|
||||||
|
'remark': 'remark',
|
||||||
|
'tags': 'tags',
|
||||||
|
'evaluation_value': 'evaluation_value',
|
||||||
|
'hidden_location': 'hidden_location',
|
||||||
|
'sub_loc_id': 'sub_loc_id',
|
||||||
|
'subcategory': 'subcategory'
|
||||||
|
}
|
||||||
|
|
||||||
|
for source_field, target_field in field_mapping.items():
|
||||||
|
if source_field == 'hidden_location':
|
||||||
|
count = Location2025.objects.filter(**{target_field: True}).count()
|
||||||
|
else:
|
||||||
|
count = Location2025.objects.exclude(**{f'{target_field}__isnull': True}).exclude(**{target_field: ''}).count()
|
||||||
|
migrated_stats[source_field] = count
|
||||||
|
print(f'{target_field}データあり: {count}件')
|
||||||
|
|
||||||
|
# 座標検証
|
||||||
|
with_location = Location2025.objects.exclude(location__isnull=True).count()
|
||||||
|
with_lat_lng = Location2025.objects.exclude(longitude__isnull=True).exclude(latitude__isnull=True).count()
|
||||||
|
print(f'location座標あり: {with_location}件')
|
||||||
|
print(f'lat/lng座標あり: {with_lat_lng}件')
|
||||||
|
|
||||||
|
# 必須フィールド検証
|
||||||
|
with_event = Location2025.objects.exclude(event__isnull=True).count()
|
||||||
|
with_cp_name = Location2025.objects.exclude(cp_name__isnull=True).exclude(cp_name='').count()
|
||||||
|
print(f'eventリンクあり: {with_event}件')
|
||||||
|
print(f'cp_nameあり: {with_cp_name}件')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'total': total_migrated,
|
||||||
|
'fields': migrated_stats,
|
||||||
|
'with_location': with_location,
|
||||||
|
'with_lat_lng': with_lat_lng,
|
||||||
|
'with_event': with_event,
|
||||||
|
'with_cp_name': with_cp_name
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_comparison_report(source_stats, migrated_stats):
|
||||||
|
"""移行前後比較レポート"""
|
||||||
|
print('\n=== 移行前後比較レポート ===')
|
||||||
|
|
||||||
|
print(f'総件数比較:')
|
||||||
|
print(f' 移行前: {source_stats["total"]:,}件')
|
||||||
|
print(f' 移行後: {migrated_stats["total"]:,}件')
|
||||||
|
print(f' 移行率: {(migrated_stats["total"] / source_stats["total"] * 100):.1f}%')
|
||||||
|
|
||||||
|
print(f'\nフィールド別データ保持率:')
|
||||||
|
for field in source_stats['fields']:
|
||||||
|
if field in migrated_stats['fields']:
|
||||||
|
source_count = source_stats['fields'][field]
|
||||||
|
migrated_count = migrated_stats['fields'][field]
|
||||||
|
if source_count > 0:
|
||||||
|
retention_rate = (migrated_count / source_count * 100)
|
||||||
|
print(f' {field}: {migrated_count:,}/{source_count:,}件 ({retention_rate:.1f}%)')
|
||||||
|
else:
|
||||||
|
print(f' {field}: {migrated_count:,}/0件 (N/A)')
|
||||||
|
|
||||||
|
def analyze_event_distribution():
|
||||||
|
"""イベント別分布分析"""
|
||||||
|
print('\n=== イベント別分布分析 ===')
|
||||||
|
|
||||||
|
event_stats = {}
|
||||||
|
for location in Location2025.objects.select_related('event'):
|
||||||
|
event_name = location.event.event_name if location.event else 'No Event'
|
||||||
|
event_code = location.event.event_code if location.event else 'No Code'
|
||||||
|
key = f"{event_code} ({event_name})"
|
||||||
|
event_stats[key] = event_stats.get(key, 0) + 1
|
||||||
|
|
||||||
|
# 件数順でソート
|
||||||
|
sorted_events = sorted(event_stats.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
|
print(f'総イベント数: {len(sorted_events)}件')
|
||||||
|
print(f'上位イベント:')
|
||||||
|
for i, (event_key, count) in enumerate(sorted_events[:10], 1):
|
||||||
|
print(f' {i:2d}. {event_key}: {count:,}件')
|
||||||
|
|
||||||
|
return event_stats
|
||||||
|
|
||||||
|
def sample_data_verification():
|
||||||
|
"""サンプルデータ検証"""
|
||||||
|
print('\n=== サンプルデータ検証 ===')
|
||||||
|
|
||||||
|
# 各種データパターンのサンプルを取得
|
||||||
|
samples = []
|
||||||
|
|
||||||
|
# 写真データありのサンプル
|
||||||
|
photo_sample = Location2025.objects.filter(photos__isnull=False).exclude(photos='').first()
|
||||||
|
if photo_sample:
|
||||||
|
samples.append(('写真データあり', photo_sample))
|
||||||
|
|
||||||
|
# remarkデータありのサンプル
|
||||||
|
remark_sample = Location2025.objects.filter(remark__isnull=False).exclude(remark='').first()
|
||||||
|
if remark_sample:
|
||||||
|
samples.append(('詳細説明あり', remark_sample))
|
||||||
|
|
||||||
|
# 高ポイントのサンプル
|
||||||
|
high_point_sample = Location2025.objects.filter(cp_point__gt=50).first()
|
||||||
|
if high_point_sample:
|
||||||
|
samples.append(('高ポイント', high_point_sample))
|
||||||
|
|
||||||
|
# 通常サンプル
|
||||||
|
if not samples:
|
||||||
|
normal_sample = Location2025.objects.first()
|
||||||
|
if normal_sample:
|
||||||
|
samples.append(('通常データ', normal_sample))
|
||||||
|
|
||||||
|
for sample_type, sample in samples[:3]:
|
||||||
|
print(f'\n【{sample_type}サンプル】')
|
||||||
|
print(f' CP番号: {sample.cp_number}')
|
||||||
|
print(f' CP名: {sample.cp_name}')
|
||||||
|
print(f' CPポイント: {sample.cp_point}')
|
||||||
|
print(f' フォトポイント: {sample.photo_point}')
|
||||||
|
print(f' sub_loc_id: {sample.sub_loc_id}')
|
||||||
|
print(f' subcategory: {sample.subcategory}')
|
||||||
|
|
||||||
|
# データ長を制限して表示
|
||||||
|
def truncate_text(text, max_len=30):
|
||||||
|
if not text:
|
||||||
|
return '(空)'
|
||||||
|
return text[:max_len] + '...' if len(text) > max_len else text
|
||||||
|
|
||||||
|
print(f' 写真: {truncate_text(sample.photos)}')
|
||||||
|
print(f' 動画: {truncate_text(sample.videos)}')
|
||||||
|
print(f' 詳細: {truncate_text(sample.remark)}')
|
||||||
|
print(f' タグ: {truncate_text(sample.tags)}')
|
||||||
|
print(f' 評価値: {truncate_text(sample.evaluation_value)}')
|
||||||
|
print(f' 隠し: {sample.hidden_location}')
|
||||||
|
print(f' イベント: {sample.event.event_name if sample.event else "None"}')
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""メイン実行関数"""
|
||||||
|
User = get_user_model()
|
||||||
|
default_user = User.objects.first()
|
||||||
|
|
||||||
|
print('='*60)
|
||||||
|
print('Location → Location2025 完全移行スクリプト(統計検証付き)')
|
||||||
|
print('='*60)
|
||||||
|
|
||||||
|
# 1. 移行前データ分析
|
||||||
|
source_stats = analyze_source_data()
|
||||||
|
|
||||||
|
# 2. 既存Location2025データ削除
|
||||||
|
print('\n=== 既存データクリア ===')
|
||||||
|
deleted_count = Location2025.objects.count()
|
||||||
|
Location2025.objects.all().delete()
|
||||||
|
print(f'削除済み: {deleted_count}件')
|
||||||
|
|
||||||
|
# 3. NewEvent2のevent_codeマップ作成
|
||||||
|
print('\n=== Event Code マッピング ===')
|
||||||
|
events = NewEvent2.objects.filter(event_code__isnull=False).exclude(event_code='')
|
||||||
|
event_code_map = {}
|
||||||
|
for event in events:
|
||||||
|
event_code_map[event.event_code] = event
|
||||||
|
print(f'有効なevent_code数: {len(event_code_map)}件')
|
||||||
|
|
||||||
|
# 4. データ移行実行
|
||||||
|
print('\n=== データ移行実行 ===')
|
||||||
|
locations = Location.objects.all()
|
||||||
|
processed_combinations = set()
|
||||||
|
migrated_count = 0
|
||||||
|
skipped_count = 0
|
||||||
|
error_count = 0
|
||||||
|
event_migration_stats = defaultdict(int)
|
||||||
|
|
||||||
|
for location in locations:
|
||||||
|
try:
|
||||||
|
# groupが空の場合はスキップ
|
||||||
|
if not location.group:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# location.groupに含まれるevent_codeを検索
|
||||||
|
matched_event = None
|
||||||
|
matched_event_code = None
|
||||||
|
|
||||||
|
for event_code, event in event_code_map.items():
|
||||||
|
if event_code in location.group:
|
||||||
|
matched_event = event
|
||||||
|
matched_event_code = event_code
|
||||||
|
break
|
||||||
|
|
||||||
|
# マッチするevent_codeがない場合はスキップ
|
||||||
|
if not matched_event:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# cp_number + event_idの組み合わせを確認
|
||||||
|
combination_key = (location.cp, matched_event.id)
|
||||||
|
if combination_key in processed_combinations:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# この組み合わせを処理済みとしてマーク
|
||||||
|
processed_combinations.add(combination_key)
|
||||||
|
|
||||||
|
# MultiPointからPointに変換
|
||||||
|
point_location = None
|
||||||
|
if location.geom and len(location.geom) > 0:
|
||||||
|
first_point = location.geom[0]
|
||||||
|
point_location = Point(first_point.x, first_point.y)
|
||||||
|
elif location.longitude and location.latitude:
|
||||||
|
point_location = Point(location.longitude, location.latitude)
|
||||||
|
|
||||||
|
# Location2025レコードを作成
|
||||||
|
location2025, created = Location2025.objects.update_or_create(
|
||||||
|
cp_number=location.cp,
|
||||||
|
event=matched_event,
|
||||||
|
defaults={
|
||||||
|
'cp_name': location.location_name or '',
|
||||||
|
'sub_loc_id': location.sub_loc_id or '',
|
||||||
|
'subcategory': location.subcategory or '',
|
||||||
|
'latitude': location.latitude or 0.0,
|
||||||
|
'longitude': location.longitude or 0.0,
|
||||||
|
'location': point_location,
|
||||||
|
'cp_point': int(location.checkin_point) if location.checkin_point else 0,
|
||||||
|
'photo_point': int(location.checkin_point) if location.checkin_point else 0,
|
||||||
|
'buy_point': int(location.buy_point) if location.buy_point else 0,
|
||||||
|
'checkin_radius': location.checkin_radius or 100.0,
|
||||||
|
'auto_checkin': location.auto_checkin or False,
|
||||||
|
'shop_closed': location.shop_closed or False,
|
||||||
|
'shop_shutdown': location.shop_shutdown or False,
|
||||||
|
'opening_hours': '',
|
||||||
|
'address': location.address or '',
|
||||||
|
'phone': location.phone or '',
|
||||||
|
'website': '',
|
||||||
|
'description': location.remark or '',
|
||||||
|
# 追加フィールド
|
||||||
|
'photos': location.photos or '',
|
||||||
|
'videos': location.videos or '',
|
||||||
|
'remark': location.remark or '',
|
||||||
|
'tags': location.tags or '',
|
||||||
|
'evaluation_value': location.evaluation_value or '',
|
||||||
|
'hidden_location': location.hidden_location or False,
|
||||||
|
# 管理情報
|
||||||
|
'is_active': True,
|
||||||
|
'sort_order': 0,
|
||||||
|
'csv_source_file': 'migration_from_location',
|
||||||
|
'created_by': default_user,
|
||||||
|
'updated_by': default_user,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
migrated_count += 1
|
||||||
|
event_migration_stats[matched_event_code] += 1
|
||||||
|
|
||||||
|
if migrated_count % 100 == 0:
|
||||||
|
print(f'進捗: {migrated_count:,}件完了')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f'❌ エラー: CP {location.cp} - {str(e)}')
|
||||||
|
error_count += 1
|
||||||
|
|
||||||
|
# 5. 移行結果サマリー
|
||||||
|
print(f'\n=== 移行結果サマリー ===')
|
||||||
|
print(f'移行完了: {migrated_count:,}件')
|
||||||
|
print(f'スキップ: {skipped_count:,}件')
|
||||||
|
print(f'エラー: {error_count:,}件')
|
||||||
|
print(f'総処理: {migrated_count + skipped_count + error_count:,}件')
|
||||||
|
|
||||||
|
# 6. 移行後データ検証
|
||||||
|
migrated_stats = validate_migration_data(source_stats)
|
||||||
|
|
||||||
|
# 7. 比較レポート生成
|
||||||
|
generate_comparison_report(source_stats, migrated_stats)
|
||||||
|
|
||||||
|
# 8. イベント別分布分析
|
||||||
|
event_distribution = analyze_event_distribution()
|
||||||
|
|
||||||
|
# 9. サンプルデータ検証
|
||||||
|
sample_data_verification()
|
||||||
|
|
||||||
|
# 10. 最終検証サマリー
|
||||||
|
print('\n' + '='*60)
|
||||||
|
print('🎯 移行完了検証サマリー')
|
||||||
|
print('='*60)
|
||||||
|
|
||||||
|
success_rate = (migrated_count / source_stats['total'] * 100) if source_stats['total'] > 0 else 0
|
||||||
|
print(f'✅ 総移行成功率: {success_rate:.1f}% ({migrated_count:,}/{source_stats["total"]:,}件)')
|
||||||
|
print(f'✅ エラー率: {(error_count / source_stats["total"] * 100):.1f}% ({error_count:,}件)')
|
||||||
|
print(f'✅ 最終Location2025件数: {Location2025.objects.count():,}件')
|
||||||
|
print(f'✅ 対応イベント数: {len(event_distribution)}件')
|
||||||
|
|
||||||
|
# データ品質スコア算出
|
||||||
|
quality_score = 0
|
||||||
|
if migrated_stats['with_event'] == migrated_stats['total']:
|
||||||
|
quality_score += 25 # 全てにイベントがリンクされている
|
||||||
|
if migrated_stats['with_cp_name'] >= migrated_stats['total'] * 0.95:
|
||||||
|
quality_score += 25 # 95%以上にCP名がある
|
||||||
|
if migrated_stats['fields']['photos'] >= migrated_stats['total'] * 0.8:
|
||||||
|
quality_score += 25 # 80%以上に写真データがある
|
||||||
|
if migrated_stats['fields']['remark'] >= migrated_stats['total'] * 0.8:
|
||||||
|
quality_score += 25 # 80%以上に詳細説明がある
|
||||||
|
|
||||||
|
print(f'✅ データ品質スコア: {quality_score}/100点')
|
||||||
|
|
||||||
|
if quality_score >= 90:
|
||||||
|
print('🏆 優秀:本格運用準備完了')
|
||||||
|
elif quality_score >= 70:
|
||||||
|
print('🥉 良好:運用可能レベル')
|
||||||
|
elif quality_score >= 50:
|
||||||
|
print('⚠️ 要改善:一部データ補完推奨')
|
||||||
|
else:
|
||||||
|
print('❌ 要対応:データ品質に課題あり')
|
||||||
|
|
||||||
|
print('\n✅ 全フィールド対応の完全データ移行が正常に完了しました')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
64
migrate_sub_fields_to_location2025.py
Normal file
64
migrate_sub_fields_to_location2025.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
LocationからLocation2025へsub_loc_idとsubcategoryを移行するスクリプト
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import django
|
||||||
|
|
||||||
|
# Djangoの設定
|
||||||
|
sys.path.append('/app')
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from rog.models import Location, Location2025
|
||||||
|
|
||||||
|
def migrate_sub_fields():
|
||||||
|
"""LocationからLocation2025にsub_loc_idとsubcategoryを移行"""
|
||||||
|
|
||||||
|
print("LocationからLocation2025への移行を開始します...")
|
||||||
|
|
||||||
|
# Locationデータを取得
|
||||||
|
locations = Location.objects.all()
|
||||||
|
print(f"移行対象のLocationレコード数: {locations.count()}")
|
||||||
|
|
||||||
|
# Location2025データとマッチングして更新
|
||||||
|
updated_count = 0
|
||||||
|
not_found_count = 0
|
||||||
|
|
||||||
|
for location in locations:
|
||||||
|
# cp_numberとcp_nameでLocation2025を検索
|
||||||
|
try:
|
||||||
|
# location_idをcp_numberとして検索
|
||||||
|
location2025_records = Location2025.objects.filter(
|
||||||
|
cp_number=location.location_id,
|
||||||
|
cp_name__icontains=location.location_name[:50] # 名前の部分一致
|
||||||
|
)
|
||||||
|
|
||||||
|
if location2025_records.exists():
|
||||||
|
for location2025 in location2025_records:
|
||||||
|
# フィールドが空の場合のみ更新
|
||||||
|
if not location2025.sub_loc_id and location.sub_loc_id:
|
||||||
|
location2025.sub_loc_id = location.sub_loc_id
|
||||||
|
|
||||||
|
if not location2025.subcategory and location.subcategory:
|
||||||
|
location2025.subcategory = location.subcategory
|
||||||
|
|
||||||
|
location2025.save()
|
||||||
|
updated_count += 1
|
||||||
|
print(f"✓ 更新: CP{location.location_id} - {location.location_name[:30]}...")
|
||||||
|
else:
|
||||||
|
not_found_count += 1
|
||||||
|
print(f"✗ 未発見: CP{location.location_id} - {location.location_name[:30]}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"エラー (CP{location.location_id}): {str(e)}")
|
||||||
|
|
||||||
|
print(f"\n移行完了:")
|
||||||
|
print(f" 更新レコード数: {updated_count}")
|
||||||
|
print(f" 未発見レコード数: {not_found_count}")
|
||||||
|
print(f" 元レコード数: {locations.count()}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
migrate_sub_fields()
|
||||||
@ -1002,7 +1002,8 @@ admin.site.register(EventUser, admin.ModelAdmin)
|
|||||||
#admin.site.register(ShapeFileLocations, admin.ModelAdmin)
|
#admin.site.register(ShapeFileLocations, admin.ModelAdmin)
|
||||||
|
|
||||||
#admin.site.register(CustomUser, UserAdminConfig)
|
#admin.site.register(CustomUser, UserAdminConfig)
|
||||||
admin.site.register(templocation, TempLocationAdmin)
|
# 古いtemplocationは無効化 - Location2025を使用
|
||||||
|
#admin.site.register(templocation, TempLocationAdmin)
|
||||||
admin.site.register(GoalImages, admin.ModelAdmin)
|
admin.site.register(GoalImages, admin.ModelAdmin)
|
||||||
admin.site.register(CheckinImages, admin.ModelAdmin)
|
admin.site.register(CheckinImages, admin.ModelAdmin)
|
||||||
|
|
||||||
@ -1130,9 +1131,10 @@ class Location2025Admin(LeafletGeoAdmin):
|
|||||||
|
|
||||||
return redirect('..')
|
return redirect('..')
|
||||||
|
|
||||||
# フォーム表示
|
# フォーム表示 - Location2025システム用
|
||||||
from .models import NewEvent2
|
from .models import NewEvent2
|
||||||
events = NewEvent2.objects.filter(event_active=True).order_by('-created_at')
|
# statusフィールドベースでアクティブなイベントを取得
|
||||||
|
events = NewEvent2.objects.filter(status='public').order_by('-start_datetime')
|
||||||
|
|
||||||
return render(request, 'admin/location2025/upload_csv.html', {
|
return render(request, 'admin/location2025/upload_csv.html', {
|
||||||
'events': events,
|
'events': events,
|
||||||
|
|||||||
22
rog/migrations/0008_add_status_field.py
Normal file
22
rog/migrations/0008_add_status_field.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Generated manually to add status field to NewEvent2
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rog', '0007_auto_20250829_1836'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='newevent2',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[('public', 'Public'), ('private', 'Private'), ('draft', 'Draft'), ('closed', 'Closed')],
|
||||||
|
default='draft',
|
||||||
|
help_text='イベントステータス',
|
||||||
|
max_length=20
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
34
rog/migrations/0009_add_fields_to_models.py
Normal file
34
rog/migrations/0009_add_fields_to_models.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Generated manually to add missing timestamp fields to gpscheckin
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rog', '0008_add_status_field'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gpscheckin',
|
||||||
|
name='created_at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, default=timezone.now),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='gpscheckin',
|
||||||
|
name='updated_at',
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='sub_loc_id',
|
||||||
|
field=models.CharField(blank=True, max_length=2048, null=True, verbose_name='サブロケーションID'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='subcategory',
|
||||||
|
field=models.CharField(blank=True, max_length=2048, null=True, verbose_name='サブカテゴリ'),
|
||||||
|
),
|
||||||
|
]
|
||||||
43
rog/migrations/0010_add_missing_fields_to_location2025.py
Normal file
43
rog/migrations/0010_add_missing_fields_to_location2025.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Generated manually on 2025-08-30
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rog', '0009_add_fields_to_models'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='photos',
|
||||||
|
field=models.CharField(max_length=2048, blank=True, null=True, verbose_name='写真'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='videos',
|
||||||
|
field=models.CharField(max_length=2048, blank=True, null=True, verbose_name='動画'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='remark',
|
||||||
|
field=models.TextField(blank=True, null=True, verbose_name='備考'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='tags',
|
||||||
|
field=models.CharField(max_length=2048, blank=True, null=True, verbose_name='タグ'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='evaluation_value',
|
||||||
|
field=models.CharField(max_length=255, blank=True, null=True, verbose_name='評価値'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='location2025',
|
||||||
|
name='hidden_location',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='隠しロケーション'),
|
||||||
|
),
|
||||||
|
]
|
||||||
0
rog/migrations/__init__.py
Normal file
0
rog/migrations/__init__.py
Normal file
@ -1091,6 +1091,8 @@ class Location2025(models.Model):
|
|||||||
cp_number = models.IntegerField(_('CP番号'), db_index=True)
|
cp_number = models.IntegerField(_('CP番号'), db_index=True)
|
||||||
event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, verbose_name=_('イベント'))
|
event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, verbose_name=_('イベント'))
|
||||||
cp_name = models.CharField(_('CP名'), max_length=255)
|
cp_name = models.CharField(_('CP名'), max_length=255)
|
||||||
|
sub_loc_id = models.CharField(_('サブロケーションID'), max_length=2048, blank=True, null=True)
|
||||||
|
subcategory = models.CharField(_('サブカテゴリ'), max_length=2048, blank=True, null=True)
|
||||||
|
|
||||||
# 位置情報
|
# 位置情報
|
||||||
latitude = models.FloatField(_('緯度'), null=True, blank=True)
|
latitude = models.FloatField(_('緯度'), null=True, blank=True)
|
||||||
@ -1117,6 +1119,14 @@ class Location2025(models.Model):
|
|||||||
website = models.URLField(_('ウェブサイト'), blank=True, null=True)
|
website = models.URLField(_('ウェブサイト'), blank=True, null=True)
|
||||||
description = models.TextField(_('説明'), blank=True, null=True)
|
description = models.TextField(_('説明'), blank=True, null=True)
|
||||||
|
|
||||||
|
# 追加フィールド(Locationテーブルから移行)
|
||||||
|
photos = models.CharField(_('写真'), max_length=2048, blank=True, null=True)
|
||||||
|
videos = models.CharField(_('動画'), max_length=2048, blank=True, null=True)
|
||||||
|
remark = models.TextField(_('備考'), blank=True, null=True)
|
||||||
|
tags = models.CharField(_('タグ'), max_length=2048, blank=True, null=True)
|
||||||
|
evaluation_value = models.CharField(_('評価値'), max_length=255, blank=True, null=True)
|
||||||
|
hidden_location = models.BooleanField(_('隠しロケーション'), default=False)
|
||||||
|
|
||||||
# 管理情報
|
# 管理情報
|
||||||
is_active = models.BooleanField(_('有効'), default=True, db_index=True)
|
is_active = models.BooleanField(_('有効'), default=True, db_index=True)
|
||||||
sort_order = models.IntegerField(_('表示順'), default=0)
|
sort_order = models.IntegerField(_('表示順'), default=0)
|
||||||
|
|||||||
Reference in New Issue
Block a user