From 93768fa4ec5ac3a759fff50b729ae0725bb5f1a4 Mon Sep 17 00:00:00 2001 From: Akira Date: Sat, 6 Sep 2025 02:23:25 +0900 Subject: [PATCH] add Gpslog log --- custom-pg_hba.conf | 4 +- rog/views_apis/api_play.py | 93 ++++++++++++++++---- show_checkin_data_sql.py | 5 +- show_django_data.py | 169 +++++++++++++++++++++++++++++++++++++ 4 files changed, 248 insertions(+), 23 deletions(-) create mode 100644 show_django_data.py diff --git a/custom-pg_hba.conf b/custom-pg_hba.conf index 47313a4..07b9648 100644 --- a/custom-pg_hba.conf +++ b/custom-pg_hba.conf @@ -87,14 +87,14 @@ local all postgres peer # TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections only -local all all md5 +local all all peer # IPv4 local connections: host all all 127.0.0.1/32 md5 # IPv6 local connections: host all all ::1/128 md5 # Allow replication connections from localhost, by a user with the # replication privilege. -local replication all md5 +local replication all peer host replication all 127.0.0.1/32 md5 host replication all ::1/128 md5 host all all 172.0.0.0/8 md5 diff --git a/rog/views_apis/api_play.py b/rog/views_apis/api_play.py index 3dc8645..f84db3d 100755 --- a/rog/views_apis/api_play.py +++ b/rog/views_apis/api_play.py @@ -621,11 +621,15 @@ def checkin_from_rogapp(request): logger.warning(f"[CHECKIN] Location2025 model issue - ID: {request_id}, CP: {cp_number}, Error: {e}") # トランザクション開始 + logger.info(f"[GPSLOG] 🔄 Starting database transaction - ID: {request_id}") with transaction.atomic(): + logger.info(f"[GPSLOG] 🔒 Database transaction started successfully - ID: {request_id}") + # S3に画像をアップロードし、S3 URLを取得 s3_image_url = image_url if image_url and S3_AVAILABLE and s3_uploader: try: + logger.info(f"[GPSLOG] 📤 Starting S3 upload - ID: {request_id}, image_size: {len(image_url)} chars") s3_image_url = s3_uploader.upload_checkin_image( image_data=image_url, event_code=entry.event.event_name, @@ -633,32 +637,61 @@ def checkin_from_rogapp(request): cp_number=cp_number ) logger.info(f"[CHECKIN] S3 upload - Original: {image_url[:50]}..., S3: {s3_image_url}") + logger.info(f"[GPSLOG] ✅ S3 upload completed - ID: {request_id}, new_url: {s3_image_url[:50]}...") except Exception as e: logger.error(f"[CHECKIN] S3 upload failed, using original URL: {e}") + logger.error(f"[GPSLOG] ❌ S3 upload failed - ID: {request_id}, error: {e}") s3_image_url = image_url elif image_url: logger.info(f"[CHECKIN] S3 not available, using original URL") + logger.info(f"[GPSLOG] ℹ️ S3 not available - ID: {request_id}, using original URL") # serial_numberを自動生成(既存の最大値+1) + logger.info(f"[GPSLOG] 🔢 Calculating serial number for zekken: {entry.zekken_number}, event: {entry.event.event_name}") max_serial = GpsLog.objects.filter( zekken_number=entry.zekken_number, event_code=entry.event.event_name ).aggregate(max_serial=Max('serial_number'))['max_serial'] or 0 + new_serial = max_serial + 1 + logger.info(f"[GPSLOG] 📊 Serial number calculation - max_existing: {max_serial}, new_serial: {new_serial}") + + # GpsLogテーブルへの書き込み準備 + gpslog_data = { + 'serial_number': new_serial, + 'zekken_number': entry.zekken_number, + 'event_code': entry.event.event_name, + 'cp_number': cp_number, + 'image_address': s3_image_url, # S3 URLを保存 + 'checkin_time': timezone.now(), + 'create_at': timezone.now(), + 'update_at': timezone.now(), + 'buy_flag': False, + 'is_service_checked': False, # Location2025にはis_service_cpがないので、デフォルトでFalse + 'colabo_company_memo': "" + } + + logger.info(f"[GPSLOG] 📝 Preparing GpsLog record - ID: {request_id}") + logger.info(f"[GPSLOG] 🏷️ Data: serial={new_serial}, zekken={entry.zekken_number}, event={entry.event.event_name}, cp={cp_number}") + logger.info(f"[GPSLOG] 🖼️ Image: has_image={bool(s3_image_url)}, url_length={len(s3_image_url) if s3_image_url else 0}") + logger.info(f"[GPSLOG] ⏰ Timestamps: checkin_time={gpslog_data['checkin_time']}") + # チェックポイント登録(S3 URLを使用) - checkpoint = GpsLog.objects.create( - serial_number=max_serial + 1, - zekken_number=entry.zekken_number, - event_code=entry.event.event_name, - cp_number=cp_number, - image_address=s3_image_url, # S3 URLを保存 - checkin_time=timezone.now(), - create_at=timezone.now(), - update_at=timezone.now(), - buy_flag=False, - is_service_checked=False, # Location2025にはis_service_cpがないので、デフォルトでFalse - colabo_company_memo="" - ) + try: + checkpoint = GpsLog.objects.create(**gpslog_data) + logger.info(f"[GPSLOG] ✅ GpsLog record created successfully - ID: {checkpoint.id}, request_id: {request_id}") + logger.info(f"[GPSLOG] 🎯 Created record details - DB_ID: {checkpoint.id}, serial: {checkpoint.serial_number}, checkin_time: {checkpoint.checkin_time}") + + # 作成されたレコードの検証 + if checkpoint.id: + logger.info(f"[GPSLOG] 🔍 Verification - Record exists in database with ID: {checkpoint.id}") + else: + logger.warning(f"[GPSLOG] ⚠️ Warning - Record created but ID is None") + + except Exception as create_error: + logger.error(f"[GPSLOG] ❌ Failed to create GpsLog record - ID: {request_id}, Error: {create_error}") + logger.error(f"[GPSLOG] 📊 Failed data: {gpslog_data}") + raise create_error # 獲得ポイントの計算(Location2025から取得) point_value = event_cp.checkin_point if event_cp else 0 @@ -669,27 +702,34 @@ def checkin_from_rogapp(request): "total_points": point_value } + logger.info(f"[GPSLOG] 🎯 Point calculation - base_points: {point_value}, bonus_points: {bonus_points}") + # カメラボーナス計算 if image_url and event_cp and hasattr(event_cp, 'evaluation_value'): if event_cp.evaluation_value == "1": # 写真撮影必須ポイント bonus_points += 5 scoring_breakdown["camera_bonus"] = 5 scoring_breakdown["total_points"] += 5 + logger.info(f"[GPSLOG] 📸 Camera bonus applied - additional_points: 5, total: {scoring_breakdown['total_points']}") logger.info(f"[CHECKIN] ✅ SUCCESS - Team: {team_name}, Zekken: {entry.zekken_number}, CP: {cp_number}, Points: {point_value}, Bonus: {bonus_points}, Time: {checkpoint.checkin_time}, Has Image: {bool(image_url)}, Buy Flag: {buy_flag}, Client IP: {client_ip}, User: {user_info}") + logger.info(f"[GPSLOG] 🏆 FINAL RESULT - GpsLog_ID: {checkpoint.id}, Serial: {checkpoint.serial_number}, Total_Points: {scoring_breakdown['total_points']}, Request_ID: {request_id}") # 競技状態を更新(スタート・ゴール以外のチェックイン時) if cp_number not in ["START", "GOAL", -2, -1]: entry.rogaining_counted = True entry.last_checkin_time = checkpoint.checkin_time entry.save() - logger.info(f"[CHECKIN] ✅ Competition status updated - rogaining_counted: True") + logger.info(f"[CHECKIN] ✅ Competition status updated - rogaining_counted: True, last_checkin_time: {checkpoint.checkin_time}") + logger.info(f"[GPSLOG] 🎮 Entry updated - entry_id: {entry.id}, rogaining_counted: {entry.rogaining_counted}") + else: + logger.info(f"[GPSLOG] ℹ️ Special checkpoint ({cp_number}) - Entry status not updated") # 拡張情報があれば保存 if gps_coordinates or camera_metadata: try: from ..models import CheckinExtended - CheckinExtended.objects.create( + extended_record = CheckinExtended.objects.create( gpslog=checkpoint, gps_latitude=gps_coordinates.get('latitude'), gps_longitude=gps_coordinates.get('longitude'), @@ -700,10 +740,14 @@ def checkin_from_rogapp(request): bonus_points=bonus_points, scoring_breakdown=scoring_breakdown ) + logger.info(f"[GPSLOG] 📋 Extended info saved - CheckinExtended_ID: {extended_record.id}") except Exception as ext_error: - logger.warning(f"Failed to save extended checkin info: {ext_error}") + logger.warning(f"[GPSLOG] ⚠️ Failed to save extended checkin info: {ext_error}") + else: + logger.info(f"[GPSLOG] ℹ️ No extended GPS/camera data to save") - return Response({ + # レスポンス作成 + response_data = { "status": "OK", "message": "チェックポイントが正常に登録されました", "team_name": team_name, @@ -722,7 +766,13 @@ def checkin_from_rogapp(request): "ready_for_goal": entry.ready_for_goal, "is_at_goal": entry.is_at_goal } - }) + } + + logger.info(f"[GPSLOG] 📤 Creating response - ID: {request_id}") + logger.info(f"[GPSLOG] 📊 Response summary - checkpoint_id: {checkpoint.id}, total_points: {scoring_breakdown['total_points']}, status: OK") + logger.info(f"[GPSLOG] 🔄 Transaction completed successfully - ID: {request_id}") + + return Response(response_data) except Exception as e: # より詳細なエラー情報をログに記録 @@ -737,6 +787,13 @@ def checkin_from_rogapp(request): } logger.error(f"[CHECKIN] ❌ DETAILED ERROR: {error_details}") + logger.error(f"[GPSLOG] ❌ GpsLog creation failed - ID: {request_id if 'request_id' in locals() else 'Unknown'}, Error: {type(e).__name__}: {str(e)}") + + # GpsLogトランザクション失敗の詳細 + if 'checkpoint' in locals(): + logger.error(f"[GPSLOG] 💾 Transaction state - checkpoint created: True, checkpoint_id: {getattr(checkpoint, 'id', 'Unknown')}") + else: + logger.error(f"[GPSLOG] 💾 Transaction state - checkpoint created: False, transaction rolled back") # より具体的なエラーメッセージを返す if "データベース" in str(e).lower() or "database" in str(e).lower(): diff --git a/show_checkin_data_sql.py b/show_checkin_data_sql.py index 2884db6..431b290 100644 --- a/show_checkin_data_sql.py +++ b/show_checkin_data_sql.py @@ -15,7 +15,7 @@ def run_db_query(query): """ try: cmd = [ - 'docker-compose', 'exec', '-T', 'db', + 'docker-compose', 'exec', '-T', 'postgres-db', 'psql', '-U', 'admin', '-d', 'rogdb', '-c', query ] @@ -23,8 +23,7 @@ def run_db_query(query): result = subprocess.run( cmd, capture_output=True, - text=True, - cwd='/Volumes/PortableSSD1TB/main/GifuTabi/rogaining_srv_exdb-2/rogaining_srv' + text=True ) if result.returncode == 0: diff --git a/show_django_data.py b/show_django_data.py new file mode 100644 index 0000000..f447933 --- /dev/null +++ b/show_django_data.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +""" +Django管理コマンド: 最近のGpsLogとCheckinImagesデータを表示 +docker-compose exec app python manage.py shell で実行 +""" + +import os +import sys +import django +from datetime import datetime, timedelta + +# Django設定 +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +django.setup() + +from django.utils import timezone +from rog.models import GpsLog, CheckinImages, CustomUser +from django.db import connection + +def show_recent_data(days=7): + """ + 最近のチェックインデータを表示 + + Args: + days (int): 過去何日分のデータを表示するか + """ + # 基準日時を計算 + since_date = timezone.now() - timedelta(days=days) + + print(f"=== 過去{days}日間のチェックインデータ ===") + print(f"検索期間: {since_date.strftime('%Y-%m-%d %H:%M:%S')} 以降") + print("-" * 80) + + # GpsLogの最近のデータ + print("\n📍 GpsLog (チェックイン記録)") + print("-" * 50) + + recent_gpslogs = GpsLog.objects.filter( + checkin_time__gte=since_date + ).order_by('-checkin_time')[:20] + + if recent_gpslogs: + print(f"件数: {len(recent_gpslogs)}件") + print() + print("ID | 時刻 | ゼッケン | イベント | CP | 画像URL") + print("-" * 80) + + for log in recent_gpslogs: + image_status = "✅" if log.image_address else "❌" + image_url_preview = log.image_address[:40] + "..." if log.image_address and len(log.image_address) > 40 else log.image_address or "" + + print(f"{log.id:5d} | {log.checkin_time.strftime('%m-%d %H:%M:%S'):15s} | {log.zekken_number:8s} | {log.event_code:11s} | {log.cp_number:4s} | {image_status} {image_url_preview}") + else: + print("データなし") + + # CheckinImagesの最近のデータ + print(f"\n🖼️ CheckinImages (写真記録)") + print("-" * 50) + + recent_images = CheckinImages.objects.filter( + checkintime__gte=since_date + ).order_by('-checkintime')[:20] + + if recent_images: + print(f"件数: {len(recent_images)}件") + print() + print("ID | 時刻 | ユーザー | チーム名 | イベント | CP | 画像ファイル") + print("-" * 100) + + for img in recent_images: + user_name = img.user.email[:12] if img.user else "Unknown" + team_name = img.team_name[:12] if img.team_name else "" + event_code = img.event_code[:11] if img.event_code else "" + image_file = str(img.checkinimage)[:30] + "..." if len(str(img.checkinimage)) > 30 else str(img.checkinimage) + + print(f"{img.id:5d} | {img.checkintime.strftime('%m-%d %H:%M:%S'):15s} | {user_name:12s} | {team_name:12s} | {event_code:11s} | {img.cp_number:4d} | {image_file}") + else: + print("データなし") + +def show_summary_stats(): + """ + チェックインデータの統計情報を表示 + """ + print(f"\n📊 データベース統計") + print("-" * 50) + + # 各テーブルの総件数 + gpslog_total = GpsLog.objects.count() + checkinimages_total = CheckinImages.objects.count() + + print(f"GpsLog 総件数: {gpslog_total:,}") + print(f"CheckinImages 総件数: {checkinimages_total:,}") + + # 最新・最古のデータ + if gpslog_total > 0: + latest_gpslog = GpsLog.objects.order_by('-checkin_time').first() + oldest_gpslog = GpsLog.objects.order_by('checkin_time').first() + print(f"GpsLog 最新: {latest_gpslog.checkin_time.strftime('%Y-%m-%d %H:%M:%S') if latest_gpslog else 'N/A'}") + print(f"GpsLog 最古: {oldest_gpslog.checkin_time.strftime('%Y-%m-%d %H:%M:%S') if oldest_gpslog else 'N/A'}") + + if checkinimages_total > 0: + latest_image = CheckinImages.objects.order_by('-checkintime').first() + oldest_image = CheckinImages.objects.order_by('checkintime').first() + print(f"CheckinImages 最新: {latest_image.checkintime.strftime('%Y-%m-%d %H:%M:%S') if latest_image else 'N/A'}") + print(f"CheckinImages 最古: {oldest_image.checkintime.strftime('%Y-%m-%d %H:%M:%S') if oldest_image else 'N/A'}") + + # 画像有りのGpsLog件数 + gpslog_with_images = GpsLog.objects.exclude(image_address__isnull=True).exclude(image_address='').count() + print(f"画像付きGpsLog: {gpslog_with_images:,} / {gpslog_total:,} ({gpslog_with_images/gpslog_total*100:.1f}%)" if gpslog_total > 0 else "画像付きGpsLog: 0") + +def show_event_breakdown(): + """ + イベント別のチェックイン件数を表示 + """ + print(f"\n🎯 イベント別チェックイン件数") + print("-" * 50) + + # GpsLogのイベント別集計 + print("GpsLog:") + with connection.cursor() as cursor: + cursor.execute(""" + SELECT event_code, COUNT(*) as count + FROM rog_gpslog + GROUP BY event_code + ORDER BY count DESC + LIMIT 10 + """) + + for row in cursor.fetchall(): + print(f" {row[0]:15s}: {row[1]:,}件") + + # CheckinImagesのイベント別集計 + print("\nCheckinImages:") + with connection.cursor() as cursor: + cursor.execute(""" + SELECT event_code, COUNT(*) as count + FROM rog_checkinimages + GROUP BY event_code + ORDER BY count DESC + LIMIT 10 + """) + + for row in cursor.fetchall(): + print(f" {row[0]:15s}: {row[1]:,}件") + +# 引数処理 +days = 7 +if len(sys.argv) > 1: + try: + days = int(sys.argv[1]) + except ValueError: + print("⚠️ 日数は数値で指定してください") + days = 7 + +print("🏃‍♂️ ロゲイニング チェックインデータ表示ツール") +print("=" * 80) + +try: + # データ表示 + show_recent_data(days) + show_summary_stats() + show_event_breakdown() + + print(f"\n✅ 完了") + +except Exception as e: + print(f"❌ エラーが発生しました: {e}") + import traceback + traceback.print_exc()