Files
rogaining_srv/rog/views_apis/api_ranking.py
2025-08-20 19:15:19 +09:00

375 lines
14 KiB
Python
Executable File
Raw Permalink 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.

# 既存のインポート部分に追加
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from rog.models import NewEvent2, Entry
import logging
from django.db.models import F, Count
from django.utils import timezone
logger = logging.getLogger(__name__)
"""
解説
この実装では以下の処理を行っています:
1.イベントコードとクラス名のパラメータを受け取ります
2.パラメータが不足している場合はエラーを返します
3.指定されたイベントの存在を確認します
4.指定クラスに属する、ゴール済みのチームをスコア降順で取得します
5.各チームについて以下の情報を収集します:
- 順位1から始まる
- チーム基本情報(名前、ゼッケン番号)
- スコア
- レース時間(スタートからゴールまでの時間)
- スタート時間・ゴール時間
- チェックポイント通過数
- オーナー情報(ユーザー名、メールアドレス)
6.ランキングの要約情報も提供します:
- イベント情報
- 指定クラスのチーム総数
- ゴール済みチーム数
- 未ゴールチーム数(スタート済みだがゴールしていないチーム)
この実装により、イベント管理者やユーザーはリアルタイムのランキング情報を確認できます。
特に結果発表や途中経過の確認に有用です。スコア順にソートされているため、現在の順位が一目でわかります。
"""
@api_view(['GET'])
def get_ranking(request):
"""
指定クラスのランキングを取得
パラメータ:
- class: クラス名
- event: イベントコード
"""
logger.info("get_ranking called")
# リクエストからパラメータを取得
class_name = request.query_params.get('class')
event_code = request.query_params.get('event')
logger.debug(f"Parameters: class={class_name}, event={event_code}")
# パラメータ検証
if not all([class_name, event_code]):
logger.warning("Missing required parameters")
return Response({
"status": "ERROR",
"message": "クラス名とイベントコードが必要です"
}, status=status.HTTP_400_BAD_REQUEST)
try:
# イベントの存在確認
event = NewEvent2.objects.filter(event_name=event_code).first()
if not event:
logger.warning(f"Event not found: {event_code}")
return Response({
"status": "ERROR",
"message": "指定されたイベントが見つかりません"
}, status=status.HTTP_404_NOT_FOUND)
# 指定クラスのゴール済みチームを取得(スコア降順)
teams = Entry.objects.filter(
event=event,
class_name=class_name,
goal_info__isnull=False
).order_by('-goal_info__score')
ranking_data = []
for i, team in enumerate(teams):
# チームのスタート情報を取得
start_time = None
if hasattr(team, 'start_info') and team.start_info:
start_time = team.start_info.start_time
# チームのゴール情報を取得
goal_time = None
score = 0
if hasattr(team, 'goal_info') and team.goal_info:
goal_time = team.goal_info.goal_time
score = team.goal_info.score or 0
# レース時間を計算
race_time = None
if start_time and goal_time:
race_seconds = (goal_time - start_time).total_seconds()
# 時間:分:秒の形式にフォーマット
hours, remainder = divmod(int(race_seconds), 3600)
minutes, seconds = divmod(remainder, 60)
race_time = f"{hours:02}:{minutes:02}:{seconds:02}"
# チェックポイント数を取得
cp_count = 0
try:
from rog.models import GpsLog
cp_count = GpsLog.objects.filter(entry=team).count()
except:
pass
# ランキングデータに追加
team_data = {
"rank": i + 1, # 1-basedのランキング
"team_name": team.team_name,
"zekken_number": team.zekken_number,
"score": score,
"race_time": race_time,
"start_time": start_time.strftime("%Y-%m-%d %H:%M:%S") if start_time else None,
"goal_time": goal_time.strftime("%Y-%m-%d %H:%M:%S") if goal_time else None,
"checkpoint_count": cp_count
}
# オーナー情報があれば追加
if hasattr(team, 'owner') and team.owner:
team_data["owner_name"] = team.owner.username
team_data["owner_email"] = team.owner.email
ranking_data.append(team_data)
# 未ゴールチームの数を取得
not_finished_count = Entry.objects.filter(
event=event,
class_name=class_name,
start_info__isnull=False,
goal_info__isnull=True
).count()
# 登録チーム総数を取得
total_teams = Entry.objects.filter(
event=event,
class_name=class_name
).count()
# イベント情報を取得
event_info = {
"event_name": event.event_name,
"event_description": getattr(event, 'description', None),
"event_date": getattr(event, 'event_date', None)
}
if hasattr(event, 'event_date') and event.event_date:
event_info["event_date"] = event.event_date.strftime("%Y-%m-%d")
# 現在の日時
current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
return Response({
"status": "OK",
"event": event_info,
"class_name": class_name,
"total_teams": total_teams,
"finished_teams": len(ranking_data),
"not_finished_teams": not_finished_count,
"rankings": ranking_data,
"timestamp": current_time
})
except Exception as e:
logger.error(f"Error in get_ranking: {str(e)}")
return Response({
"status": "ERROR",
"message": "サーバーエラーが発生しました"
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# 既存のインポート部分に追加
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from rog.models import NewEvent2, Entry
import logging
from django.db.models import F, Count, Q
from django.utils import timezone
logger = logging.getLogger(__name__)
"""
解説
この実装では以下の処理を行っています:
1.イベントコードのパラメータを受け取ります
2.パラメータが不足している場合はエラーを返します
3.指定されたイベントの存在を確認します
4.イベント内の全クラスを取得します
5.各クラスごとに以下の処理を行います:
- ゴール済みチームをスコア降順で取得し、上位3件に絞り込みます
- 各チームの基本情報(ランク、名前、ゼッケン番号、スコア、レース時間、チェックポイント数)を収集します
- クラスの統計情報(総チーム数、ゴール済みチーム数)を計算します
- クラスデータとチームデータを階層化して格納します
6.イベント全体の統計情報も提供します:
- 全クラス数
- 全チーム数
- ゴール済みチーム数
- 未ゴールチーム数(スタート済みだがゴールしていないチーム)
この実装により、リアルタイムで全クラスのトップ3ランキングを確認できます。
大会のスコアボードやリザルト公開ページなどに利用できます。
1つのリクエストで全クラスの情報が取得できるため、複数のクラスを表示するページで効率的に情報を取得できます。
"""
@api_view(['GET'])
def all_ranking_top3(request):
"""
指定イベントの全クラスにおけるトップ3選手のランキングを取得
パラメータ:
- event: イベントコード
"""
logger.info("all_ranking_top3 called")
# リクエストからパラメータを取得
event_code = request.query_params.get('event')
logger.debug(f"Parameters: event={event_code}")
# パラメータ検証
if not event_code:
logger.warning("Missing required event parameter")
return Response({
"status": "ERROR",
"message": "イベントコードが必要です"
}, status=status.HTTP_400_BAD_REQUEST)
try:
# イベントの存在確認
event = NewEvent2.objects.filter(event_name=event_code).first()
if not event:
logger.warning(f"Event not found: {event_code}")
return Response({
"status": "ERROR",
"message": "指定されたイベントが見つかりません"
}, status=status.HTTP_404_NOT_FOUND)
# イベント内の全クラスを取得
all_classes = Entry.objects.filter(
event=event
).values_list('class_name', flat=True).distinct()
class_rankings = []
for class_name in all_classes:
# 指定クラスのゴール済みチームを取得スコア降順、上位3件
teams = Entry.objects.filter(
event=event,
class_name=class_name,
goal_info__isnull=False
).order_by('-goal_info__score')[:3] # 上位3件
team_rankings = []
for i, team in enumerate(teams):
# チームのスタート情報を取得
start_time = None
if hasattr(team, 'start_info') and team.start_info:
start_time = team.start_info.start_time
# チームのゴール情報を取得
goal_time = None
score = 0
if hasattr(team, 'goal_info') and team.goal_info:
goal_time = team.goal_info.goal_time
score = team.goal_info.score or 0
# レース時間を計算
race_time = None
if start_time and goal_time:
race_seconds = (goal_time - start_time).total_seconds()
# 時間:分:秒の形式にフォーマット
hours, remainder = divmod(int(race_seconds), 3600)
minutes, seconds = divmod(remainder, 60)
race_time = f"{hours:02}:{minutes:02}:{seconds:02}"
# チェックポイント数を取得
cp_count = 0
try:
from rog.models import GpsLog
cp_count = GpsLog.objects.filter(entry=team).count()
except:
pass
# チームデータ
team_data = {
"rank": i + 1, # 1-basedのランキング
"team_name": team.team_name,
"zekken_number": team.zekken_number,
"score": score,
"race_time": race_time,
"checkpoint_count": cp_count
}
team_rankings.append(team_data)
# クラスの総チーム数
total_teams_in_class = Entry.objects.filter(
event=event,
class_name=class_name
).count()
# ゴール済みチーム数
finished_teams_in_class = Entry.objects.filter(
event=event,
class_name=class_name,
goal_info__isnull=False
).count()
class_data = {
"class_name": class_name,
"total_teams": total_teams_in_class,
"finished_teams": finished_teams_in_class,
"top_teams": team_rankings
}
class_rankings.append(class_data)
# イベント情報を取得
event_info = {
"event_name": event.event_name,
"event_description": getattr(event, 'description', None),
"event_date": getattr(event, 'event_date', None)
}
if hasattr(event, 'event_date') and event.event_date:
event_info["event_date"] = event.event_date.strftime("%Y-%m-%d")
# 未ゴールチームの数をイベント全体で取得
not_finished_count = Entry.objects.filter(
event=event,
start_info__isnull=False,
goal_info__isnull=True
).count()
# ゴール済みチームの数をイベント全体で取得
finished_count = Entry.objects.filter(
event=event,
goal_info__isnull=False
).count()
# 登録チーム総数をイベント全体で取得
total_teams = Entry.objects.filter(
event=event
).count()
# 現在の日時
current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
return Response({
"status": "OK",
"event": event_info,
"total_classes": len(class_rankings),
"total_teams": total_teams,
"finished_teams": finished_count,
"not_finished_teams": not_finished_count,
"rankings_by_class": class_rankings,
"timestamp": current_time
})
except Exception as e:
logger.error(f"Error in all_ranking_top3: {str(e)}")
return Response({
"status": "ERROR",
"message": "サーバーエラーが発生しました"
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)