diff --git a/rog/views_apis/api_edit.py b/rog/views_apis/api_edit.py index cafd8fc..9b6c699 100755 --- a/rog/views_apis/api_edit.py +++ b/rog/views_apis/api_edit.py @@ -4,7 +4,7 @@ from datetime import datetime, timezone from rest_framework.decorators import api_view from rest_framework.response import Response 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 uuid import os @@ -218,25 +218,36 @@ def start_checkin(request): "message": "指定されたゼッケン番号のチームが見つかりません" }, status=status.HTTP_404_NOT_FOUND) - # 既にスタート済みかチェック - if hasattr(entry, 'start_info'): - logger.warning(f"Team {entry.team_name} (zekken: {zekken_number}) already started at {entry.start_info.start_time}") + # 既にスタート済みかチェック(GpsLogでSTARTレコードを確認) + existing_start = GpsLog.objects.filter( + 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({ "status": "WARNING", "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 }) # トランザクション開始 with transaction.atomic(): - # スタート情報を登録 - start_info = TeamStart.objects.create( + # スタート情報をGpsLogとして登録 + start_info = GpsLog.objects.create( 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({ "status": "OK", @@ -244,7 +255,7 @@ def start_checkin(request): "team_name": entry.team_name, "zekken_number": zekken_number, "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: @@ -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}") # チームがスタートしているか確認(オプション) - 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 - TeamStart.objects.create(entry=entry, start_time=timezone.now()) + GpsLog.objects.create( + 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}") # チェックポイントリストを解析 @@ -764,22 +788,41 @@ def goal_checkin(request): "message": "指定されたゼッケン番号のチームが見つかりません" }, status=status.HTTP_404_NOT_FOUND) - # チームがスタートしているか確認 - if not hasattr(entry, 'start_info'): + # チームがスタートしているか確認(GpsLogでSTARTレコードを確認) + start_record = GpsLog.objects.filter( + entry=entry, + cp_number="START", + serial_number=0 + ).first() + + if not start_record: # 管理画面からの操作なので、自動的にスタートさせる - from rog.models import TeamStart - TeamStart.objects.create(entry=entry, start_time=timezone.now()) + GpsLog.objects.create( + 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})") - # 既にゴールしているかチェック - if hasattr(entry, 'goal_info'): - logger.warning(f"Team {entry.team_name} (zekken: {zekken_number}) already reached goal at {entry.goal_info.goal_time}") + # 既にゴールしているかチェック(GpsLogでGOALレコードを確認) + existing_goal = GpsLog.objects.filter( + 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({ "status": "WARNING", "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, - "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 scoreboard_url = f"{settings.MEDIA_URL}scoreboards/{scoreboard_filename}" - # ゴール情報を登録 - goal_info = TeamGoal.objects.create( + # ゴール情報をGpsLogとして登録 + goal_info = GpsLog.objects.create( entry=entry, - goal_time=goal_time, - score=score, - scoreboard_url=scoreboard_url + cp_number="GOAL", + serial_number=9999, + 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}") diff --git a/rog/views_apis/api_play.py b/rog/views_apis/api_play.py index f73fd2e..95824bb 100755 --- a/rog/views_apis/api_play.py +++ b/rog/views_apis/api_play.py @@ -2,7 +2,7 @@ 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, TeamStart, TeamGoal +from rog.models import NewEvent2, Entry, Location2025, GpsLog from rog.models import GpsLog import logging from django.db.models import F, Q @@ -242,8 +242,11 @@ def start_from_rogapp(request): # リクエストからパラメータを取得 event_code = request.data.get('event_code') 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]): @@ -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'}')") - # 既にスタート済みかチェック - if hasattr(entry, 'start_info'): - 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}") + # 既にスタート済みかチェック(GpsLogでSTARTレコードを確認) + existing_start = GpsLog.objects.filter( + 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({ "status": "WARNING", "message": "このチームは既にスタートしています", @@ -291,16 +300,21 @@ def start_from_rogapp(request): # トランザクション開始 with transaction.atomic(): - # スタート情報を登録 - start_info = TeamStart.objects.create( + # スタート情報をGpsLogとして登録 + start_info = GpsLog.objects.create( 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 - 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({ "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}") - # チームがスタートしているか確認 - if not hasattr(entry, 'start_info'): + # チームがスタートしているか確認(GpsLogでSTARTレコードを確認) + 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") return Response({ "status": "ERROR", "message": "このチームはまだスタートしていません。先にスタート処理を行ってください。" }, status=status.HTTP_400_BAD_REQUEST) - # 既にゴールしているかチェック - if hasattr(entry, 'goal_info'): - logger.warning(f"Team {team_name} already reached goal at {entry.goal_info.goal_time}") + # 既にゴールしているかチェック(GpsLogでGOALレコードを確認) + existing_goal = GpsLog.objects.filter( + 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({ "status": "WARNING", "message": "このチームは既にゴールしています", - "goal_time": entry.goal_info.goal_time.strftime("%Y-%m-%d %H:%M:%S"), - "scoreboard_url": entry.goal_info.scoreboard_url + "goal_time": existing_goal.checkin_time.strftime("%Y-%m-%d %H:%M:%S"), + "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 scoreboard_url = f"{settings.MEDIA_URL}scoreboards/{scoreboard_filename}" - # ゴール情報を登録 - goal_info = TeamGoal.objects.create( + # ゴール情報をGpsLogとして登録 + goal_info = GpsLog.objects.create( entry=entry, - goal_time=goal_time, - image_url=image_url, - score=score, - scoreboard_url=scoreboard_url + cp_number="GOAL", + serial_number=9999, # ゴール記録の固定シリアル番号 + latitude=0.0, # ゴールポイントの座標(固定) + 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}")