Files
rogaining_srv/rog/views_apis/api_qr_points.py
2025-09-04 10:34:48 +09:00

299 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
QRコードサービスポイント処理API
このモジュールは、QRコードを使用したサービスポイントの登録・処理を行います。
"""
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from rog.models import NewEvent2, Entry, Location2025, GpsCheckin
from django.db import transaction
from django.utils import timezone
from datetime import datetime
import logging
import json
import uuid
logger = logging.getLogger(__name__)
@api_view(['POST'])
def submit_qr_points(request):
"""
QRコードサービスポイント登録API
パラメータ:
- event_code: イベントコード
- team_name: チーム名
- qr_code_data: QRコードデータ
- latitude: 緯度(オプション)
- longitude: 経度(オプション)
- image: 画像データ(オプション)
- cp_number: チェックポイント番号(オプション)
"""
# リクエストIDを生成してログで追跡できるようにする
request_id = str(uuid.uuid4())[:8]
logger.info(f"[QR_SUBMIT] 🚀 Starting QR points submission - ID: {request_id}")
# クライアント情報を取得
user_agent = request.META.get('HTTP_USER_AGENT', 'Unknown')
client_ip = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR', 'Unknown')
user_info = str(request.user) if request.user.is_authenticated else 'Anonymous'
# リクエストからパラメータを取得
event_code = request.data.get('event_code')
team_name = request.data.get('team_name')
qr_code_data = request.data.get('qr_code_data')
latitude = request.data.get('latitude')
longitude = request.data.get('longitude')
image_data = request.data.get('image')
cp_number = request.data.get('cp_number')
# 📋 パラメータをログ出力(デバッグ用)
logger.info(f"[QR_SUBMIT] ID: {request_id} - 📋 Request Parameters:")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 🏷️ Event Code: '{event_code}'")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 👥 Team Name: '{team_name}'")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 📱 QR Code Data: '{qr_code_data}'")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 📍 Latitude: {latitude}")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 🌍 Longitude: {longitude}")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 🏁 CP Number: {cp_number}")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 📸 Has Image: {image_data is not None}")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 🌐 Client IP: {client_ip}")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 👤 User: {user_info}")
logger.info(f"[QR_SUBMIT] ID: {request_id} - 🔧 User Agent: {user_agent[:100]}...")
# 全リクエストデータをログ出力(セキュリティ上重要でないデータのみ)
safe_data = {k: v for k, v in request.data.items() if k not in ['image', 'password']}
logger.info(f"[QR_SUBMIT] ID: {request_id} - 📊 Full Request Data: {json.dumps(safe_data, ensure_ascii=False, indent=2)}")
cp_number = request.data.get('cp_number')
# 📊 詳細なパラメータログ
logger.info(f"[QR_SUBMIT] 📊 DETAILED PARAMS - ID: {request_id}")
logger.info(f"[QR_SUBMIT] 🏷️ Basic params: event_code='{event_code}', team_name='{team_name}'")
logger.info(f"[QR_SUBMIT] 📱 QR params: qr_code_data='{qr_code_data}', cp_number={cp_number}")
logger.info(f"[QR_SUBMIT] 🌍 Location params: lat={latitude}, lng={longitude}")
logger.info(f"[QR_SUBMIT] 🖼️ Image params: has_image={bool(image_data)}, image_size={len(str(image_data)) if image_data else 0}")
logger.info(f"[QR_SUBMIT] 📱 Client params: user_agent='{user_agent[:100]}...', client_ip='{client_ip}'")
logger.info(f"[QR_SUBMIT] 🔐 Auth params: user_authenticated={request.user.is_authenticated}, user='{user_info}'")
# 全リクエストデータをダンプ(デバッグ用)
try:
request_data_safe = {}
for key, value in request.data.items():
if key == 'image' and value:
request_data_safe[key] = f"[IMAGE_DATA:{len(str(value))}chars]"
else:
request_data_safe[key] = value
logger.info(f"[QR_SUBMIT] 📥 FULL REQUEST DATA: {json.dumps(request_data_safe, ensure_ascii=False, indent=2)}")
except Exception as e:
logger.warning(f"[QR_SUBMIT] Failed to log request data: {e}")
# パラメータ検証
if not all([event_code, team_name, qr_code_data]):
logger.warning(f"[QR_SUBMIT] ❌ Missing required parameters - ID: {request_id}")
return Response({
"status": "ERROR",
"message": "イベントコード、チーム名、QRコードデータが必要です",
"request_id": request_id
}, status=status.HTTP_400_BAD_REQUEST)
try:
with transaction.atomic():
# イベントの存在確認
event = NewEvent2.objects.filter(event_name=event_code).first()
if not event:
logger.warning(f"[QR_SUBMIT] ❌ Event not found: {event_code} - ID: {request_id}")
return Response({
"status": "ERROR",
"message": f"指定されたイベント '{event_code}' が見つかりません",
"request_id": request_id
}, status=status.HTTP_404_NOT_FOUND)
# チームの存在確認
entry = Entry.objects.filter(
event=event,
team__team_name=team_name
).first()
if not entry:
logger.warning(f"[QR_SUBMIT] ❌ Team not found: {team_name} in event: {event_code} - ID: {request_id}")
return Response({
"status": "ERROR",
"message": f"指定されたチーム '{team_name}' がイベント '{event_code}' に見つかりません",
"request_id": request_id
}, status=status.HTTP_404_NOT_FOUND)
# QRコードデータの解析
try:
if isinstance(qr_code_data, str):
# JSON文字列の場合はパース
if qr_code_data.startswith('{'):
qr_data = json.loads(qr_code_data)
else:
# 単純な文字列の場合
qr_data = {"code": qr_code_data}
else:
qr_data = qr_code_data
logger.info(f"[QR_SUBMIT] 📱 Parsed QR data: {qr_data} - ID: {request_id}")
except json.JSONDecodeError as e:
logger.warning(f"[QR_SUBMIT] ❌ Invalid QR code data format: {e} - ID: {request_id}")
return Response({
"status": "ERROR",
"message": "QRコードデータの形式が正しくありません",
"request_id": request_id
}, status=status.HTTP_400_BAD_REQUEST)
# チェックポイント情報の取得cp_numberが指定されている場合
location = None
if cp_number:
location = Location2025.objects.filter(
event_id=event.id,
cp_number=cp_number
).first()
if location:
logger.info(f"[QR_SUBMIT] 📍 Found location: CP{cp_number} - {location.cp_name} - ID: {request_id}")
else:
logger.warning(f"[QR_SUBMIT] ⚠️ Location not found for CP{cp_number} - ID: {request_id}")
# QRポイント登録処理
current_time = timezone.now()
# GpsCheckinレコードを作成QRコード情報を含む
checkin_data = {
'event': event,
'entry': entry,
'zekken_number': entry.zekken_number,
'cp_number': cp_number or 0, # cp_numberが指定されていない場合は0
'checkin_time': current_time,
'is_service_checked': True, # QRコードはサービスポイントとして扱う
}
# 位置情報が提供されている場合は追加
if latitude and longitude:
checkin_data['latitude'] = float(latitude)
checkin_data['longitude'] = float(longitude)
logger.info(f"[QR_SUBMIT] 🌍 GPS coordinates recorded: {latitude}, {longitude} - ID: {request_id}")
# QRコードデータを格納JSONフィールドがある場合
if hasattr(GpsCheckin, 'qr_code_data'):
checkin_data['qr_code_data'] = qr_data
# 画像データを格納URLまたはパスの場合
if image_data:
if hasattr(GpsCheckin, 'image_url'):
checkin_data['image_url'] = image_data
logger.info(f"[QR_SUBMIT] 🖼️ Image data recorded - ID: {request_id}")
# レコードを作成
gps_checkin = GpsCheckin.objects.create(**checkin_data)
# ポイント計算
point_value = 0
if location:
point_value = location.cp_point or 0
# 成功レスポンス
response_data = {
"status": "OK",
"message": "QRコードサービスポイントが正常に登録されました",
"request_id": request_id,
"data": {
"event_code": event_code,
"team_name": team_name,
"zekken_number": entry.zekken_number,
"cp_number": cp_number,
"point_value": point_value,
"checkin_time": current_time.isoformat(),
"qr_code_processed": True,
"has_location": bool(latitude and longitude),
"has_image": bool(image_data),
"checkin_id": gps_checkin.id if gps_checkin else None
}
}
logger.info(f"[QR_SUBMIT] ✅ SUCCESS - Team: {team_name}, Zekken: {entry.zekken_number}, CP: {cp_number}, Points: {point_value}, QR: {bool(qr_code_data)}, Client IP: {client_ip}, User: {user_info} - ID: {request_id}")
return Response(response_data, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"[QR_SUBMIT] ❌ EXCEPTION - ID: {request_id}, Error: {str(e)}", exc_info=True)
return Response({
"status": "ERROR",
"message": "QRコードサービスポイント処理中にエラーが発生しました",
"request_id": request_id,
"error_detail": str(e) if logger.getEffectiveLevel() <= logging.DEBUG else None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@api_view(['GET'])
def qr_points_status(request):
"""
QRポイント処理状況確認API
パラメータ:
- event_code: イベントコード
- team_name: チーム名(オプション)
"""
event_code = request.query_params.get('event_code')
team_name = request.query_params.get('team_name')
logger.info(f"[QR_STATUS] QR points status check - event: {event_code}, team: {team_name}")
if not event_code:
return Response({
"status": "ERROR",
"message": "イベントコードが必要です"
}, status=status.HTTP_400_BAD_REQUEST)
try:
# イベントの存在確認
event = NewEvent2.objects.filter(event_name=event_code).first()
if not event:
return Response({
"status": "ERROR",
"message": f"指定されたイベント '{event_code}' が見つかりません"
}, status=status.HTTP_404_NOT_FOUND)
# QRコード関連のチェックイン状況を取得
query = GpsCheckin.objects.filter(event=event, is_service_checked=True)
if team_name:
query = query.filter(entry__team__team_name=team_name)
qr_checkins = query.order_by('-checkin_time')[:50] # 最新50件
checkin_list = []
for checkin in qr_checkins:
checkin_list.append({
"checkin_id": checkin.id,
"team_name": checkin.entry.team.team_name,
"zekken_number": checkin.zekken_number,
"cp_number": checkin.cp_number,
"checkin_time": checkin.checkin_time.isoformat(),
"has_qr_data": hasattr(checkin, 'qr_code_data') and bool(checkin.qr_code_data),
"has_location": bool(checkin.latitude and checkin.longitude),
"is_service_checked": checkin.is_service_checked
})
return Response({
"status": "OK",
"message": "QRポイント状況を取得しました",
"data": {
"event_code": event_code,
"total_count": len(checkin_list),
"checkins": checkin_list
}
}, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"[QR_STATUS] Error getting QR points status: {str(e)}", exc_info=True)
return Response({
"status": "ERROR",
"message": "QRポイント状況取得中にエラーが発生しました",
"error_detail": str(e) if logger.getEffectiveLevel() <= logging.DEBUG else None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)