Fix start/goal processes

This commit is contained in:
2025-09-02 17:10:33 +09:00
parent d6b40bd0f8
commit 0acaa6ea1f
2 changed files with 130 additions and 49 deletions

View File

@ -4,7 +4,7 @@ from datetime import datetime, timezone
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rog.models import Location2025, NewEvent2, Entry, GpsLog, TeamStart, TeamGoal from rog.models import Location2025, NewEvent2, Entry, GpsLog
import logging import logging
import uuid import uuid
import os import os
@ -218,25 +218,36 @@ def start_checkin(request):
"message": "指定されたゼッケン番号のチームが見つかりません" "message": "指定されたゼッケン番号のチームが見つかりません"
}, status=status.HTTP_404_NOT_FOUND) }, status=status.HTTP_404_NOT_FOUND)
# 既にスタート済みかチェック # 既にスタート済みかチェックGpsLogでSTARTレコードを確認
if hasattr(entry, 'start_info'): existing_start = GpsLog.objects.filter(
logger.warning(f"Team {entry.team_name} (zekken: {zekken_number}) already started at {entry.start_info.start_time}") entry=entry,
cp_number="START",
serial_number=0
).first()
if existing_start:
logger.warning(f"Team {entry.team_name} (zekken: {zekken_number}) already started at {existing_start.checkin_time}")
return Response({ return Response({
"status": "WARNING", "status": "WARNING",
"message": "このチームは既にスタートしています", "message": "このチームは既にスタートしています",
"start_time": entry.start_info.start_time.strftime("%Y-%m-%d %H:%M:%S"), "start_time": existing_start.checkin_time.strftime("%Y-%m-%d %H:%M:%S"),
"team_name": entry.team_name "team_name": entry.team_name
}) })
# トランザクション開始 # トランザクション開始
with transaction.atomic(): with transaction.atomic():
# スタート情報を登録 # スタート情報をGpsLogとして登録
start_info = TeamStart.objects.create( start_info = GpsLog.objects.create(
entry=entry, entry=entry,
start_time=timezone.now() cp_number="START",
serial_number=0,
latitude=0.0,
longitude=0.0,
checkin_time=timezone.now(),
extra_data={"source": "admin_start"}
) )
logger.info(f"Team {entry.team_name} (zekken: {zekken_number}) started at {start_info.start_time}") logger.info(f"Team {entry.team_name} (zekken: {zekken_number}) started at {start_info.checkin_time}")
return Response({ return Response({
"status": "OK", "status": "OK",
@ -244,7 +255,7 @@ def start_checkin(request):
"team_name": entry.team_name, "team_name": entry.team_name,
"zekken_number": zekken_number, "zekken_number": zekken_number,
"event_code": event_code, "event_code": event_code,
"start_time": start_info.start_time.strftime("%Y-%m-%d %H:%M:%S") "start_time": start_info.checkin_time.strftime("%Y-%m-%d %H:%M:%S")
}) })
except Exception as e: except Exception as e:
@ -331,11 +342,24 @@ def add_checkin(request):
logger.info(f"[ADMIN_CHECKIN] ✅ Team found - ID: {request_id}, team_name: '{entry.team_name}', zekken: {zekken_number}, entry_id: {entry.id}") logger.info(f"[ADMIN_CHECKIN] ✅ Team found - ID: {request_id}, team_name: '{entry.team_name}', zekken: {zekken_number}, entry_id: {entry.id}")
# チームがスタートしているか確認(オプション) # チームがスタートしているか確認(オプション)
if not hasattr(entry, 'start_info'): start_record = GpsLog.objects.filter(
entry=entry,
cp_number="START",
serial_number=0
).first()
if not start_record:
# スタート情報がない場合は自動的にスタートさせる # スタート情報がない場合は自動的にスタートさせる
# 注意: 管理画面からの操作なので、自動スタートを許可 # 注意: 管理画面からの操作なので、自動スタートを許可
from rog.models import TeamStart GpsLog.objects.create(
TeamStart.objects.create(entry=entry, start_time=timezone.now()) entry=entry,
cp_number="START",
serial_number=0,
latitude=0.0,
longitude=0.0,
checkin_time=timezone.now(),
extra_data={"auto_start": True, "source": "admin_bulk_checkin"}
)
logger.info(f"[ADMIN_CHECKIN] ✅ Auto-started team - ID: {request_id}, team_name: '{entry.team_name}', zekken: {zekken_number}") logger.info(f"[ADMIN_CHECKIN] ✅ Auto-started team - ID: {request_id}, team_name: '{entry.team_name}', zekken: {zekken_number}")
# チェックポイントリストを解析 # チェックポイントリストを解析
@ -764,22 +788,41 @@ def goal_checkin(request):
"message": "指定されたゼッケン番号のチームが見つかりません" "message": "指定されたゼッケン番号のチームが見つかりません"
}, status=status.HTTP_404_NOT_FOUND) }, status=status.HTTP_404_NOT_FOUND)
# チームがスタートしているか確認 # チームがスタートしているか確認GpsLogでSTARTレコードを確認
if not hasattr(entry, 'start_info'): start_record = GpsLog.objects.filter(
entry=entry,
cp_number="START",
serial_number=0
).first()
if not start_record:
# 管理画面からの操作なので、自動的にスタートさせる # 管理画面からの操作なので、自動的にスタートさせる
from rog.models import TeamStart GpsLog.objects.create(
TeamStart.objects.create(entry=entry, start_time=timezone.now()) entry=entry,
cp_number="START",
serial_number=0,
latitude=0.0,
longitude=0.0,
checkin_time=timezone.now(),
extra_data={"auto_start": True, "source": "admin_goal"}
)
logger.info(f"Auto-started team {entry.team_name} (zekken: {zekken_number})") logger.info(f"Auto-started team {entry.team_name} (zekken: {zekken_number})")
# 既にゴールしているかチェック # 既にゴールしているかチェックGpsLogでGOALレコードを確認
if hasattr(entry, 'goal_info'): existing_goal = GpsLog.objects.filter(
logger.warning(f"Team {entry.team_name} (zekken: {zekken_number}) already reached goal at {entry.goal_info.goal_time}") entry=entry,
cp_number="GOAL",
serial_number=9999
).first()
if existing_goal:
logger.warning(f"Team {entry.team_name} (zekken: {zekken_number}) already reached goal at {existing_goal.checkin_time}")
return Response({ return Response({
"status": "WARNING", "status": "WARNING",
"message": "このチームは既にゴールしています", "message": "このチームは既にゴールしています",
"goal_time": entry.goal_info.goal_time.strftime("%Y-%m-%d %H:%M:%S"), "goal_time": existing_goal.checkin_time.strftime("%Y-%m-%d %H:%M:%S"),
"team_name": entry.team_name, "team_name": entry.team_name,
"scoreboard_url": entry.goal_info.scoreboard_url "scoreboard_url": existing_goal.extra_data.get('scoreboard_url', '') if existing_goal.extra_data else ''
}) })
# ゴール時間の処理 # ゴール時間の処理
@ -812,12 +855,18 @@ def goal_checkin(request):
# スコアボードへのURL # スコアボードへのURL
scoreboard_url = f"{settings.MEDIA_URL}scoreboards/{scoreboard_filename}" scoreboard_url = f"{settings.MEDIA_URL}scoreboards/{scoreboard_filename}"
# ゴール情報を登録 # ゴール情報をGpsLogとして登録
goal_info = TeamGoal.objects.create( goal_info = GpsLog.objects.create(
entry=entry, entry=entry,
goal_time=goal_time, cp_number="GOAL",
score=score, serial_number=9999,
scoreboard_url=scoreboard_url latitude=0.0,
longitude=0.0,
checkin_time=goal_time,
extra_data={
"score": score,
"scoreboard_url": scoreboard_url
}
) )
logger.info(f"Team {entry.team_name} (zekken: {zekken_number}) reached goal at {goal_time} with score {score}") logger.info(f"Team {entry.team_name} (zekken: {zekken_number}) reached goal at {goal_time} with score {score}")

View File

@ -2,7 +2,7 @@
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rog.models import NewEvent2, Entry, Location2025, TeamStart, TeamGoal from rog.models import NewEvent2, Entry, Location2025, GpsLog
from rog.models import GpsLog from rog.models import GpsLog
import logging import logging
from django.db.models import F, Q from django.db.models import F, Q
@ -242,8 +242,11 @@ def start_from_rogapp(request):
# リクエストからパラメータを取得 # リクエストからパラメータを取得
event_code = request.data.get('event_code') event_code = request.data.get('event_code')
team_name = request.data.get('team_name') team_name = request.data.get('team_name')
latitude = request.data.get('latitude', 0.0)
longitude = request.data.get('longitude', 0.0)
extra_data = request.data.get('extra_data', {})
logger.info(f"[COMPETITION_START] Request parameters - ID: {request_id}, event_code: '{event_code}', team_name: '{team_name}', user_agent: '{user_agent[:100]}'") logger.info(f"[COMPETITION_START] Request parameters - ID: {request_id}, event_code: '{event_code}', team_name: '{team_name}', GPS: ({latitude},{longitude}), user_agent: '{user_agent[:100]}'")
# パラメータ検証 # パラメータ検証
if not all([event_code, team_name]): if not all([event_code, team_name]):
@ -280,9 +283,15 @@ def start_from_rogapp(request):
logger.info(f"[COMPETITION_START] ✅ Team found - ID: {request_id}, Entry ID: {entry.id}, Team: '{team_name}', Zekken: {entry.zekken_number}, Category: '{entry.category.category_name if entry.category else 'N/A'}', Owner: '{entry.owner.email if entry.owner else 'N/A'}')") logger.info(f"[COMPETITION_START] ✅ Team found - ID: {request_id}, Entry ID: {entry.id}, Team: '{team_name}', Zekken: {entry.zekken_number}, Category: '{entry.category.category_name if entry.category else 'N/A'}', Owner: '{entry.owner.email if entry.owner else 'N/A'}')")
# 既にスタート済みかチェック # 既にスタート済みかチェックGpsLogでSTARTレコードを確認
if hasattr(entry, 'start_info'): existing_start = GpsLog.objects.filter(
logger.warning(f"[COMPETITION_START] ⚠️ Team already started - ID: {request_id}, team_name: '{team_name}', zekken: {entry.zekken_number}, start_time: {entry.start_info.start_time}, Client IP: {client_ip}") entry=entry,
cp_number="START",
serial_number=0
).first()
if existing_start:
logger.warning(f"[COMPETITION_START] ⚠️ Team already started - ID: {request_id}, team_name: '{team_name}', zekken: {entry.zekken_number}, start_time: {existing_start.checkin_time}, Client IP: {client_ip}")
return Response({ return Response({
"status": "WARNING", "status": "WARNING",
"message": "このチームは既にスタートしています", "message": "このチームは既にスタートしています",
@ -291,16 +300,21 @@ def start_from_rogapp(request):
# トランザクション開始 # トランザクション開始
with transaction.atomic(): with transaction.atomic():
# スタート情報を登録 # スタート情報をGpsLogとして登録
start_info = TeamStart.objects.create( start_info = GpsLog.objects.create(
entry=entry, entry=entry,
start_time=timezone.now() cp_number="START", # スタート専用のCP番号
serial_number=0, # スタート記録の固定シリアル番号
latitude=latitude if latitude else 0.0,
longitude=longitude if longitude else 0.0,
checkin_time=timezone.now(),
extra_data=extra_data
) )
# 統計情報取得 # 統計情報取得
total_checkpoints = Location2025.objects.filter(event=event).count() if hasattr(Location2025, 'event') else 0 total_checkpoints = Location2025.objects.filter(event=event).count() if hasattr(Location2025, 'event') else 0
logger.info(f"[COMPETITION_START] 🎉 SUCCESS - ID: {request_id}, Team: '{team_name}', Zekken: {entry.zekken_number}, Event: '{event_code}', Start Time: {start_info.start_time}, Total CPs Available: {total_checkpoints}, Client IP: {client_ip}, User: {user_info}") logger.info(f"[COMPETITION_START] 🎉 SUCCESS - ID: {request_id}, Team: '{team_name}', Zekken: {entry.zekken_number}, Event: '{event_code}', Start Time: {start_info.checkin_time}, GPS: ({latitude},{longitude}), Total CPs Available: {total_checkpoints}, Client IP: {client_ip}, User: {user_info}")
return Response({ return Response({
"status": "OK", "status": "OK",
@ -605,22 +619,34 @@ def goal_from_rogapp(request):
logger.info(f"[GOAL] ✅ Team found - ID: {request_id}, team_name: '{team_name}', zekken: {entry.zekken_number}, entry_id: {entry.id}") logger.info(f"[GOAL] ✅ Team found - ID: {request_id}, team_name: '{team_name}', zekken: {entry.zekken_number}, entry_id: {entry.id}")
# チームがスタートしているか確認 # チームがスタートしているか確認GpsLogでSTARTレコードを確認
if not hasattr(entry, 'start_info'): start_record = GpsLog.objects.filter(
entry=entry,
cp_number="START",
serial_number=0
).first()
if not start_record:
logger.warning(f"Team {team_name} has not started yet") logger.warning(f"Team {team_name} has not started yet")
return Response({ return Response({
"status": "ERROR", "status": "ERROR",
"message": "このチームはまだスタートしていません。先にスタート処理を行ってください。" "message": "このチームはまだスタートしていません。先にスタート処理を行ってください。"
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
# 既にゴールしているかチェック # 既にゴールしているかチェックGpsLogでGOALレコードを確認
if hasattr(entry, 'goal_info'): existing_goal = GpsLog.objects.filter(
logger.warning(f"Team {team_name} already reached goal at {entry.goal_info.goal_time}") entry=entry,
cp_number="GOAL",
serial_number=9999
).first()
if existing_goal:
logger.warning(f"Team {team_name} already reached goal at {existing_goal.checkin_time}")
return Response({ return Response({
"status": "WARNING", "status": "WARNING",
"message": "このチームは既にゴールしています", "message": "このチームは既にゴールしています",
"goal_time": entry.goal_info.goal_time.strftime("%Y-%m-%d %H:%M:%S"), "goal_time": existing_goal.checkin_time.strftime("%Y-%m-%d %H:%M:%S"),
"scoreboard_url": entry.goal_info.scoreboard_url "scoreboard_url": existing_goal.extra_data.get('scoreboard_url', '') if existing_goal.extra_data else ''
}) })
# ゴール時間の処理 # ゴール時間の処理
@ -649,13 +675,19 @@ def goal_from_rogapp(request):
# スコアボードへのURL # スコアボードへのURL
scoreboard_url = f"{settings.MEDIA_URL}scoreboards/{scoreboard_filename}" scoreboard_url = f"{settings.MEDIA_URL}scoreboards/{scoreboard_filename}"
# ゴール情報を登録 # ゴール情報をGpsLogとして登録
goal_info = TeamGoal.objects.create( goal_info = GpsLog.objects.create(
entry=entry, entry=entry,
goal_time=goal_time, cp_number="GOAL",
image_url=image_url, serial_number=9999, # ゴール記録の固定シリアル番号
score=score, latitude=0.0, # ゴールポイントの座標(固定)
scoreboard_url=scoreboard_url longitude=0.0,
checkin_time=goal_time,
image_address=image_url,
extra_data={
"score": score,
"scoreboard_url": scoreboard_url
}
) )
logger.info(f"[GOAL] ✅ SUCCESS - Team: {team_name}, Zekken: {entry.zekken_number}, Event: {event_code}, Goal Time: {goal_time}, Score: {score}, Has Image: {bool(image_url)}, Client IP: {client_ip}, User: {user_info}") logger.info(f"[GOAL] ✅ SUCCESS - Team: {team_name}, Zekken: {entry.zekken_number}, Event: {event_code}, Goal Time: {goal_time}, Score: {score}, Has Image: {bool(image_url)}, Client IP: {client_ip}, User: {user_info}")