From bcd0bee7382bee226fe5a9da518471ed3a85da6c Mon Sep 17 00:00:00 2001 From: Akira Date: Sat, 6 Sep 2025 03:21:36 +0900 Subject: [PATCH] Fix Entry issue --- monitor_app_errors.py | 166 ++++++++++++++++++++++++++++++++++++++++++ rog/serializers.py | 21 ++++-- rog/views.py | 27 ++++++- 3 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 monitor_app_errors.py diff --git a/monitor_app_errors.py b/monitor_app_errors.py new file mode 100644 index 0000000..e103313 --- /dev/null +++ b/monitor_app_errors.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +""" +スマホアプリエラー監視ツール: リアルタイムで400エラーを監視 +""" + +import subprocess +import re +import time +from datetime import datetime +import json + +def monitor_app_errors(): + """ + スマホアプリの400エラーをリアルタイム監視 + """ + print("🔍 スマホアプリエラー監視開始") + print("=" * 60) + print("Ctrl+C で停止") + print() + + try: + # docker compose logs --follow で継続監視 + process = subprocess.Popen( + ['docker', 'compose', 'logs', '--follow', 'app'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + universal_newlines=True + ) + + error_patterns = { + 'checkin_400': re.compile(r'Bad Request.*checkin_from_rogapp', re.I), + 'member_400': re.compile(r'Bad Request.*members', re.I), + 'team_400': re.compile(r'Bad Request.*teams', re.I), + 'validation_error': re.compile(r'Validation error.*({.*})', re.I), + 'checkin_start_error': re.compile(r'Team has not started yet.*team_name: \'([^\']+)\'.*cp_number: (\d+)', re.I), + 'dart_request': re.compile(r'Dart/\d+\.\d+.*"([A-Z]+)\s+([^"]+)".*(\d{3})', re.I) + } + + print(f"🎯 監視対象パターン:") + for name, pattern in error_patterns.items(): + print(f" • {name}") + print() + + while True: + line = process.stdout.readline() + if not line: + break + + timestamp = datetime.now().strftime("%H:%M:%S") + + # Dartクライアント(スマホアプリ)のリクエスト監視 + dart_match = error_patterns['dart_request'].search(line) + if dart_match and 'Dart/' in line: + method = dart_match.group(1) + path = dart_match.group(2) + status = dart_match.group(3) + + if status.startswith('4'): # 4xx エラー + print(f"❌ [{timestamp}] スマホアプリエラー: {method} {path} → HTTP {status}") + elif status.startswith('2'): # 2xx 成功 + if any(keyword in path.lower() for keyword in ['checkin', 'teams', 'members']): + print(f"✅ [{timestamp}] スマホアプリ成功: {method} {path} → HTTP {status}") + + # チェックインスタートエラー監視 + start_error_match = error_patterns['checkin_start_error'].search(line) + if start_error_match: + team_name = start_error_match.group(1) + cp_number = start_error_match.group(2) + print(f"⚠️ [{timestamp}] チェックインエラー: チーム'{team_name}'がCP{cp_number}でスタート前チェックイン試行") + print(f" 💡 解決策: 先にstart_from_rogappでスタート処理が必要") + + # バリデーションエラー監視 + validation_match = error_patterns['validation_error'].search(line) + if validation_match: + try: + error_details = validation_match.group(1) + print(f"❌ [{timestamp}] バリデーションエラー: {error_details}") + if 'date_of_birth' in error_details: + print(f" 💡 date_of_birthフィールドの問題 - MemberCreationSerializerを確認") + except: + print(f"❌ [{timestamp}] バリデーションエラー詳細を解析できませんでした") + + # 一般的な400エラー監視 + for pattern_name, pattern in error_patterns.items(): + if pattern_name in ['dart_request', 'checkin_start_error', 'validation_error']: + continue + + if pattern.search(line): + print(f"❌ [{timestamp}] {pattern_name}: {line.strip()}") + + except KeyboardInterrupt: + print(f"\n\n🛑 監視を停止しました") + process.terminate() + except Exception as e: + print(f"❌ 監視エラー: {e}") + if 'process' in locals(): + process.terminate() + +def analyze_current_issues(): + """ + 現在の問題を分析 + """ + print("📊 現在の問題分析") + print("-" * 40) + + try: + # 最近のエラーログを分析 + result = subprocess.run( + ['docker', 'compose', 'logs', '--tail=100', 'app'], + capture_output=True, + text=True + ) + + lines = result.stdout.split('\n') + + # チェックインエラーの分析 + checkin_errors = [] + validation_errors = [] + + for line in lines: + if 'Team has not started yet' in line: + match = re.search(r'team_name: \'([^\']+)\'.*cp_number: (\d+)', line) + if match: + checkin_errors.append((match.group(1), match.group(2))) + + if 'Validation error' in line and 'date_of_birth' in line: + validation_errors.append(line) + + print(f"🔍 分析結果:") + print(f" • チェックインスタートエラー: {len(checkin_errors)}件") + for team, cp in checkin_errors[-3:]: # 最新3件 + print(f" - チーム'{team}' → CP{cp}") + + print(f" • date_of_birthバリデーションエラー: {len(validation_errors)}件") + + if checkin_errors: + print(f"\n💡 推奨対策:") + print(f" 1. スマホアプリでスタート処理を実行") + print(f" 2. start_from_rogapp API の正常動作確認") + + if validation_errors: + print(f" 3. MemberCreationSerializerの再確認") + + except Exception as e: + print(f"❌ 分析エラー: {e}") + +def main(): + """ + メイン関数 + """ + print("📱 スマホアプリエラー監視ツール") + print("=" * 60) + + choice = input("選択してください:\n1. リアルタイム監視\n2. 現在の問題分析\n選択 (1/2): ") + + if choice == "1": + monitor_app_errors() + elif choice == "2": + analyze_current_issues() + else: + print("無効な選択です") + +if __name__ == "__main__": + main() diff --git a/rog/serializers.py b/rog/serializers.py index 57ab769..4d81fc3 100755 --- a/rog/serializers.py +++ b/rog/serializers.py @@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model User = get_user_model() import uuid +from datetime import datetime from django.db import IntegrityError from django.conf import settings from django.urls import reverse @@ -851,12 +852,22 @@ class EntrySerializer(serializers.ModelSerializer): def to_representation(self, instance): ret = super().to_representation(instance) - ret['team'] = TeamSerializer(instance.team).data - ret['event'] = NewEvent2Serializer(instance.event).data - ret['category'] = NewCategorySerializer(instance.category).data - ret['owner'] = CustomUserSerializer(instance.owner).data + + # instance が辞書の場合(エラー時)は基本情報のみ返す + if isinstance(instance, dict): + return ret + + # 正常な場合のみ関連オブジェクトを追加 + if hasattr(instance, 'team') and instance.team: + ret['team'] = TeamSerializer(instance.team).data + if hasattr(instance, 'event') and instance.event: + ret['event'] = NewEvent2Serializer(instance.event).data + if hasattr(instance, 'category') and instance.category: + ret['category'] = NewCategorySerializer(instance.category).data + if hasattr(instance, 'owner') and instance.owner: + ret['owner'] = CustomUserSerializer(instance.owner).data - if isinstance(ret['date'], datetime): + if isinstance(ret.get('date'), datetime): ret['date'] = ret['date'].date().isoformat() elif isinstance(ret['date'], date): ret['date'] = ret['date'].isoformat() diff --git a/rog/views.py b/rog/views.py index 294483c..2968841 100755 --- a/rog/views.py +++ b/rog/views.py @@ -1748,7 +1748,11 @@ class EntryViewSet(viewsets.ModelViewSet): else: logger.info("External system registered successfully") except Exception as e: - logger.exception(f"Error creating Entry: {str(e)}") + logger.error(f"Error creating Entry: {e}") + if hasattr(e, 'message_dict'): + logger.error(f"Validation details: {e.message_dict}") + # perform_createで例外を発生させて、createメソッドでキャッチ + raise e def create(self, request, *args, **kwargs): @@ -1768,7 +1772,26 @@ class EntryViewSet(viewsets.ModelViewSet): headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) except DjangoValidationError as e: - return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) + # バリデーションエラーの詳細をログに記録 + error_details = e.message_dict if hasattr(e, 'message_dict') else str(e) + logger.error(f"Entry validation error: {error_details}") + + # ユーザーフレンドリーなエラーメッセージを作成 + if '__all__' in error_details and '男性のみ参加可能' in str(error_details): + error_message = "選択されたカテゴリーは男性のみ参加可能です。適切なカテゴリーを選択してください。" + elif '__all__' in error_details and '女性のみ参加可能' in str(error_details): + error_message = "選択されたカテゴリーは女性のみ参加可能です。適切なカテゴリーを選択してください。" + else: + error_message = str(e) + + return Response({"error": error_message}, status=status.HTTP_400_BAD_REQUEST) + except Exception as e: + # その他のエラー + logger.exception(f"Unexpected error creating Entry: {str(e)}") + return Response( + {"error": "エントリー作成中に予期しないエラーが発生しました。しばらく後に再試行してください。"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) def register_team(self, zekken_number, event_code, team_name, category_name, password):