Files
rogaining_srv/rog/views_apis/api_photos_fixed.py
2025-08-29 18:09:32 +09:00

240 lines
9.9 KiB
Python
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, GpsCheckin, Team
import logging
from django.db.models import F, Q
from django.conf import settings
import os
from urllib.parse import urljoin
logger = logging.getLogger(__name__)
"""
解説
この実装では以下の処理を行っています:
1.2つのエンドポイントを提供しています:
- /get_photo_list - 認証なしで写真とレポートURLを取得
- /get_photo_list_prod - パスワード認証付きで同じ情報を取得
2.共通のロジックは get_team_photos 関数に集約し、以下の情報を取得します:
- チームの基本情報(名前、ゼッケン番号、クラス名)
- チェックポイント通過時の写真(時間順、サービスチェック情報含む)
- スタート写真とゴール写真(あれば)
- チームレポートのURL存在する場合
- スコアボードのURL存在する場合
- チームのスコア(ゴール済みの場合)
3.レポートとスコアボードのファイルパスを実際に確認し、存在する場合のみURLを提供します
4.写真の表示順はスタート→チェックポイント(時間順)→ゴールとなっており、チェックポイントについてはそれぞれ番号、撮影時間、サービスチェック状態などの情報も含めています
この実装により、チームは自分たちの競技中の写真やレポートを簡単に確認できます。
本番環境_prod版ではパスワード認証によりセキュリティを確保しています。
"""
@api_view(['GET'])
def get_photo_list(request):
"""
チームの写真とレポートURLを取得認証なし版
パラメータ:
- zekken: ゼッケン番号
- event: イベントコード
"""
logger.info("get_photo_list called")
# リクエストからパラメータを取得
zekken_number = request.query_params.get('zekken')
event_code = request.query_params.get('event')
logger.debug(f"Parameters: zekken={zekken_number}, event={event_code}")
# パラメータ検証
if not all([zekken_number, event_code]):
logger.warning("Missing required parameters")
return Response({
"status": "ERROR",
"message": "ゼッケン番号とイベントコードが必要です"
}, status=status.HTTP_400_BAD_REQUEST)
return get_team_photos(zekken_number, event_code)
@api_view(['GET'])
def get_photo_list_prod(request):
"""
チームの写真とレポートURLを取得認証あり版
パラメータ:
- zekken: ゼッケン番号
- pw: パスワード
- event: イベントコード
"""
logger.info("get_photo_list_prod called")
# リクエストからパラメータを取得
zekken_number = request.query_params.get('zekken')
password = request.query_params.get('pw')
event_code = request.query_params.get('event')
logger.debug(f"Parameters: zekken={zekken_number}, event={event_code}, has_password={bool(password)}")
# パラメータ検証
if not all([zekken_number, password, 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)
# チームの存在確認とパスワード検証
entry = Entry.objects.filter(
event=event,
zekken_number=zekken_number
).first()
if not entry:
logger.warning(f"Team with zekken number {zekken_number} not found in event: {event_code}")
return Response({
"status": "ERROR",
"message": "指定されたゼッケン番号のチームが見つかりません"
}, status=status.HTTP_404_NOT_FOUND)
# パスワード検証
if not hasattr(entry, 'password') or entry.password != password:
logger.warning(f"Invalid password for team: {entry.team.team_name if entry.team else 'Unknown'}")
return Response({
"status": "ERROR",
"message": "パスワードが一致しません"
}, status=status.HTTP_401_UNAUTHORIZED)
return get_team_photos(zekken_number, event_code)
except Exception as e:
logger.error(f"Error in get_photo_list_prod: {str(e)}")
return Response({
"status": "ERROR",
"message": "サーバーエラーが発生しました"
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def get_team_photos(zekken_number, event_code):
"""
チームの写真とレポートURLを取得する共通関数
"""
try:
logger.info(f"Getting photos for zekken: {zekken_number}, event: {event_code}")
# イベントの存在確認event_codeで検索
event = NewEvent2.objects.filter(event_code=event_code).first()
if not event:
logger.warning(f"Event not found with event_code: {event_code}")
# event_nameでも試してみる
event = NewEvent2.objects.filter(event_name=event_code).first()
if not event:
logger.warning(f"Event not found with event_name: {event_code}")
return Response({
"status": "ERROR",
"message": "指定されたイベントが見つかりません"
}, status=status.HTTP_404_NOT_FOUND)
logger.info(f"Found event: {event.event_name} (ID: {event.id})")
# まずEntryテーブルを確認
entry = Entry.objects.filter(
event=event,
zekken_number=zekken_number
).first()
team_name = "Unknown Team"
if entry and entry.team:
team_name = entry.team.team_name
logger.info(f"Found team in Entry: {team_name}")
else:
logger.info(f"No Entry found for zekken {zekken_number}, checking GpsCheckin for legacy data")
# GpsCheckinテーブルからチーム情報を取得レガシーデータ対応
gps_checkin_sample = GpsCheckin.objects.filter(
event_code=event_code,
zekken_number=str(zekken_number)
).first()
if gps_checkin_sample and gps_checkin_sample.team:
team_name = gps_checkin_sample.team.team_name
logger.info(f"Found team in GpsCheckin: {team_name}")
else:
team_name = f"Team {zekken_number}"
logger.info(f"No team found, using default: {team_name}")
# GpsCheckinテーブルから写真データを取得
gps_checkins = GpsCheckin.objects.filter(
event_code=event_code,
zekken_number=str(zekken_number),
image_address__isnull=False
).exclude(
image_address=''
).order_by('serial_number', 'create_at')
logger.info(f"Found {gps_checkins.count()} GPS checkins with images")
# 写真リストを作成
photos = []
for gps in gps_checkins:
if gps.image_address:
# 画像URLを構築
if gps.image_address.startswith('http'):
# 絶対URLの場合はそのまま使用
image_url = gps.image_address
else:
# 相対パスの場合はベースURLと結合
# settings.MEDIA_URLやstatic fileの設定に基づいて調整
image_url = f"/media/{gps.image_address}" if not gps.image_address.startswith('/') else gps.image_address
photo_data = {
"cp_number": gps.cp_number if gps.cp_number is not None else 0,
"photo_url": image_url,
"checkin_time": gps.create_at.strftime("%Y-%m-%d %H:%M:%S") if gps.create_at else None,
"path_order": gps.serial_number,
"buy_flag": gps.buy_flag,
"validate_location": gps.validate_location,
"points": gps.points
}
photos.append(photo_data)
logger.debug(f"Added photo: CP {gps.cp_number}, URL: {image_url}")
# チームの基本情報
team_info = {
"team_name": team_name,
"zekken_number": zekken_number,
"event_name": event.event_name,
"photo_count": len(photos)
}
# レスポンス構築
response_data = {
"status": "SUCCESS",
"message": f"写真を{len(photos)}枚取得しました",
"team_info": team_info,
"photo_list": photos
}
logger.info(f"Successfully retrieved {len(photos)} photos for team {team_name}")
return Response(response_data, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"Error in get_team_photos: {str(e)}")
return Response({
"status": "ERROR",
"message": "サーバーエラーが発生しました"
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)