From 04009f78f7f786c4ab1668627190030154207f4a Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:52:06 -0500 Subject: [PATCH] milb diagnosis --- MILB_TROUBLESHOOTING.md | 178 ++++++++++++++++++ config/config.json | 2 +- src/milb_manager.py | 10 + test/diagnose_milb_issues.py | 341 +++++++++++++++++++++++++++++++++++ test/test_milb_api.py | 139 ++++++++++++++ 5 files changed, 669 insertions(+), 1 deletion(-) create mode 100644 MILB_TROUBLESHOOTING.md create mode 100644 test/diagnose_milb_issues.py create mode 100644 test/test_milb_api.py diff --git a/MILB_TROUBLESHOOTING.md b/MILB_TROUBLESHOOTING.md new file mode 100644 index 00000000..bf1c7d26 --- /dev/null +++ b/MILB_TROUBLESHOOTING.md @@ -0,0 +1,178 @@ +# MiLB Manager Troubleshooting Guide + +## **Issue Summary** +The MiLB manager is no longer pulling accurate game information due to several factors, primarily the current offseason period. + +## **Root Causes** + +### 1. **Primary Issue: MiLB Offseason** +- **Problem**: MiLB season runs from **April to September** +- **Current Status**: We're in January 2025 (offseason) +- **Impact**: No regular season games are scheduled during offseason +- **Solution**: Enable test mode for offseason testing + +### 2. **Secondary Issues** +- **API Endpoint Changes**: MLB Stats API endpoints may have changed +- **Sport ID Updates**: Some sport IDs might be outdated +- **Team Mapping**: Team abbreviations may have changed + +## **Solutions Implemented** + +### **Immediate Fix: Enable Test Mode** +```json +{ + "milb": { + "test_mode": true + } +} +``` + +### **Code Improvements** +1. **Season Awareness**: Added offseason detection +2. **Better Logging**: More informative error messages +3. **Test Mode Enhancement**: Improved test data + +## **Diagnostic Tools Created** + +### 1. **Basic API Test** +```bash +python test/test_milb_api.py +``` + +### 2. **Comprehensive Diagnostic** +```bash +python test/diagnose_milb_issues.py +``` + +## **Testing the Fixes** + +### **Step 1: Run Diagnostic** +```bash +cd /path/to/LEDMatrix +python test/diagnose_milb_issues.py +``` + +### **Step 2: Test with Test Mode** +1. Ensure `test_mode: true` in config +2. Restart the display system +3. Check if test games appear + +### **Step 3: Verify API (When Season Returns)** +```bash +python test/test_milb_api.py +``` + +## **Expected Behavior** + +### **During Offseason (October-March)** +- No real games found +- Test mode shows sample games +- Logs indicate offseason status + +### **During Season (April-September)** +- Real games should be found +- Live games display correctly +- Upcoming games show properly + +## **Configuration Options** + +### **Test Mode** +```json +{ + "milb": { + "test_mode": true, + "enabled": true + } +} +``` + +### **Season Override (For Testing)** +```json +{ + "milb": { + "test_mode": true, + "force_season": true + } +} +``` + +## **Common Issues and Solutions** + +### **Issue: No Games Found** +- **Cause**: Offseason or API issues +- **Solution**: Enable test mode + +### **Issue: API Errors** +- **Cause**: Network or endpoint issues +- **Solution**: Check internet connection and API status + +### **Issue: Wrong Team Names** +- **Cause**: Team mapping outdated +- **Solution**: Update `milb_team_mapping.json` + +### **Issue: Wrong Sport IDs** +- **Cause**: MLB API changes +- **Solution**: Update sport IDs in config + +## **Monitoring and Logs** + +### **Key Log Messages** +- `"MiLB is currently in offseason"` - Normal during offseason +- `"Using test mode data for MiLB"` - Test mode active +- `"No games returned from API"` - API issue or offseason + +### **Debug Mode** +Enable debug logging to see detailed API calls: +```python +logger.setLevel(logging.DEBUG) +``` + +## **Future Improvements** + +### **Planned Enhancements** +1. **Season Schedule Integration**: Use official season dates +2. **API Fallback**: Multiple API endpoints +3. **Caching Improvements**: Better cache management +4. **Error Recovery**: Automatic retry mechanisms + +### **Configuration Enhancements** +```json +{ + "milb": { + "season_start_month": 4, + "season_end_month": 9, + "api_fallback": true, + "cache_duration": 3600 + } +} +``` + +## **Contact and Support** + +For additional issues: +1. Run the diagnostic tools +2. Check the logs for specific errors +3. Verify network connectivity +4. Test API endpoints directly + +## **Quick Reference** + +### **Enable Test Mode** +```bash +# Edit config/config.json +# Change "test_mode": false to "test_mode": true +``` + +### **Run Diagnostics** +```bash +python test/diagnose_milb_issues.py +``` + +### **Test API Directly** +```bash +python test/test_milb_api.py +``` + +### **Check Season Status** +- **April-September**: Season active +- **October-March**: Offseason (use test mode) \ No newline at end of file diff --git a/config/config.json b/config/config.json index 0ded2037..e8d9958c 100644 --- a/config/config.json +++ b/config/config.json @@ -329,7 +329,7 @@ "enabled": true, "live_priority": true, "live_game_duration": 30, - "test_mode": false, + "test_mode": true, "update_interval_seconds": 3600, "live_update_interval": 30, "recent_update_interval": 3600, diff --git a/src/milb_manager.py b/src/milb_manager.py index ab070a7b..13a062ae 100644 --- a/src/milb_manager.py +++ b/src/milb_manager.py @@ -346,6 +346,16 @@ class BaseMiLBManager: } } + # Check if we're in MiLB season (April-September) + now = datetime.now() + current_month = now.month + in_season = 4 <= current_month <= 9 + + if not in_season: + self.logger.info("MiLB is currently in offseason (October-March). No games expected.") + self.logger.info("Consider enabling test_mode for offseason testing.") + return {} + # MiLB league sport IDs (configurable) sport_ids = self.milb_config.get('sport_ids', [10, 11, 12, 13, 14, 15]) # Mexican, AAA, AA, A+, A, Rookie diff --git a/test/diagnose_milb_issues.py b/test/diagnose_milb_issues.py new file mode 100644 index 00000000..453988bf --- /dev/null +++ b/test/diagnose_milb_issues.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python3 +""" +Comprehensive diagnostic script for MiLB manager issues +""" + +import requests +import json +import sys +import os +from datetime import datetime, timedelta, timezone + +# Add the src directory to the path so we can import the managers +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +def test_milb_api_directly(): + """Test the MiLB API directly to see what's available.""" + print("=" * 60) + print("TESTING MiLB API DIRECTLY") + print("=" * 60) + + # MiLB league sport IDs + sport_ids = [10, 11, 12, 13, 14, 15] # Mexican, AAA, AA, A+, A, Rookie + + # Get dates for the next 7 days + now = datetime.now(timezone.utc) + dates = [] + for i in range(-1, 8): # Yesterday + 7 days + date = now + timedelta(days=i) + dates.append(date.strftime("%Y-%m-%d")) + + print(f"Checking dates: {dates}") + print(f"Checking sport IDs: {sport_ids}") + + all_games = {} + api_errors = [] + + for date in dates: + for sport_id in sport_ids: + try: + url = f"http://statsapi.mlb.com/api/v1/schedule?sportId={sport_id}&date={date}" + print(f"\nFetching MiLB games for sport ID {sport_id}, date: {date}") + + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + } + + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + + data = response.json() + + if not data.get('dates'): + print(f" ❌ No dates data for sport ID {sport_id}") + continue + + if not data['dates'][0].get('games'): + print(f" ❌ No games found for sport ID {sport_id}") + continue + + games = data['dates'][0]['games'] + print(f" ✅ Found {len(games)} games for sport ID {sport_id}") + + for game in games: + game_pk = game['gamePk'] + + home_team_name = game['teams']['home']['team']['name'] + away_team_name = game['teams']['away']['team']['name'] + + home_abbr = game['teams']['home']['team'].get('abbreviation', home_team_name[:3].upper()) + away_abbr = game['teams']['away']['team'].get('abbreviation', away_team_name[:3].upper()) + + status_obj = game['status'] + status_state = status_obj.get('abstractGameState', 'Preview') + detailed_state = status_obj.get('detailedState', '').lower() + + # Check if it's a favorite team (TAM from config) + favorite_teams = ['TAM'] + is_favorite = (home_abbr in favorite_teams or away_abbr in favorite_teams) + + if is_favorite: + print(f" ⭐ FAVORITE TEAM GAME: {away_abbr} @ {home_abbr}") + print(f" Status: {detailed_state} -> {status_state}") + print(f" Scores: {game['teams']['away'].get('score', 0)} - {game['teams']['home'].get('score', 0)}") + + # Store game data + game_data = { + 'id': game_pk, + 'away_team': away_abbr, + 'home_team': home_abbr, + 'away_score': game['teams']['away'].get('score', 0), + 'home_score': game['teams']['home'].get('score', 0), + 'status': detailed_state, + 'status_state': status_state, + 'start_time': game['gameDate'], + 'is_favorite': is_favorite, + 'sport_id': sport_id + } + + all_games[game_pk] = game_data + + except Exception as e: + error_msg = f"Error fetching MiLB games for sport ID {sport_id}, date {date}: {e}" + print(f" ❌ {error_msg}") + api_errors.append(error_msg) + + # Summary + print(f"\n{'='*60}") + print(f"API TEST SUMMARY:") + print(f"Total games found: {len(all_games)}") + print(f"API errors: {len(api_errors)}") + + favorite_games = [g for g in all_games.values() if g['is_favorite']] + print(f"Favorite team games: {len(favorite_games)}") + + live_games = [g for g in all_games.values() if g['status'] == 'in progress'] + print(f"Live games: {len(live_games)}") + + upcoming_games = [g for g in all_games.values() if g['status'] in ['scheduled', 'preview']] + print(f"Upcoming games: {len(upcoming_games)}") + + final_games = [g for g in all_games.values() if g['status'] == 'final'] + print(f"Final games: {len(final_games)}") + + if favorite_games: + print(f"\nFavorite team games:") + for game in favorite_games: + print(f" {game['away_team']} @ {game['home_team']} - {game['status']} ({game['status_state']})") + + if api_errors: + print(f"\nAPI Errors:") + for error in api_errors[:5]: # Show first 5 errors + print(f" {error}") + + return all_games, api_errors + +def test_team_mapping(): + """Test the team mapping file.""" + print("\n" + "=" * 60) + print("TESTING TEAM MAPPING") + print("=" * 60) + + try: + mapping_path = os.path.join('assets', 'sports', 'milb_logos', 'milb_team_mapping.json') + with open(mapping_path, 'r') as f: + team_mapping = json.load(f) + + print(f"✅ Team mapping file loaded successfully") + print(f"Total teams in mapping: {len(team_mapping)}") + + # Check for TAM team + tam_found = False + for team_name, data in team_mapping.items(): + if data.get('abbreviation') == 'TAM': + print(f"✅ Found TAM team: {team_name}") + tam_found = True + break + + if not tam_found: + print(f"❌ TAM team not found in mapping!") + + # Check for some common teams + common_teams = ['Toledo Mud Hens', 'Buffalo Bisons', 'Tampa Tarpons'] + for team in common_teams: + if team in team_mapping: + abbr = team_mapping[team]['abbreviation'] + print(f"✅ Found {team}: {abbr}") + else: + print(f"❌ Not found: {team}") + + return team_mapping + + except Exception as e: + print(f"❌ Error loading team mapping: {e}") + return None + +def test_configuration(): + """Test the configuration settings.""" + print("\n" + "=" * 60) + print("TESTING CONFIGURATION") + print("=" * 60) + + try: + config_path = os.path.join('config', 'config.json') + with open(config_path, 'r') as f: + config = json.load(f) + + milb_config = config.get('milb', {}) + + print(f"✅ Configuration file loaded successfully") + print(f"MiLB enabled: {milb_config.get('enabled', False)}") + print(f"Favorite teams: {milb_config.get('favorite_teams', [])}") + print(f"Test mode: {milb_config.get('test_mode', False)}") + print(f"Sport IDs: {milb_config.get('sport_ids', [10, 11, 12, 13, 14, 15])}") + print(f"Live update interval: {milb_config.get('live_update_interval', 30)}") + print(f"Recent update interval: {milb_config.get('recent_update_interval', 3600)}") + print(f"Upcoming update interval: {milb_config.get('upcoming_update_interval', 3600)}") + + # Check display modes + display_modes = milb_config.get('display_modes', {}) + print(f"Display modes:") + for mode, enabled in display_modes.items(): + print(f" {mode}: {enabled}") + + return milb_config + + except Exception as e: + print(f"❌ Error loading configuration: {e}") + return None + +def test_season_timing(): + """Check if we're in MiLB season.""" + print("\n" + "=" * 60) + print("TESTING SEASON TIMING") + print("=" * 60) + + now = datetime.now() + current_month = now.month + current_year = now.year + + print(f"Current date: {now.strftime('%Y-%m-%d')}") + print(f"Current month: {current_month}") + + # MiLB season typically runs from April to September + if 4 <= current_month <= 9: + print(f"✅ Currently in MiLB season (April-September)") + else: + print(f"❌ Currently OUTSIDE MiLB season (April-September)") + print(f" This could explain why no games are found!") + + # Check if we're in offseason + if current_month in [1, 2, 3, 10, 11, 12]: + print(f"⚠️ MiLB is likely in offseason - no games expected") + + return 4 <= current_month <= 9 + +def test_cache_manager(): + """Test the cache manager functionality.""" + print("\n" + "=" * 60) + print("TESTING CACHE MANAGER") + print("=" * 60) + + try: + from cache_manager import CacheManager + + cache_manager = CacheManager() + print(f"✅ Cache manager initialized successfully") + + # Test cache operations + test_key = "test_milb_cache" + test_data = {"test": "data"} + + cache_manager.set(test_key, test_data) + print(f"✅ Cache set operation successful") + + retrieved_data = cache_manager.get(test_key) + if retrieved_data == test_data: + print(f"✅ Cache get operation successful") + else: + print(f"❌ Cache get operation failed - data mismatch") + + # Clean up test data + cache_manager.delete(test_key) + print(f"✅ Cache delete operation successful") + + return True + + except Exception as e: + print(f"❌ Error testing cache manager: {e}") + return False + +def main(): + """Run all diagnostic tests.""" + print("MiLB Manager Diagnostic Tool") + print("=" * 60) + + # Test 1: API directly + api_games, api_errors = test_milb_api_directly() + + # Test 2: Team mapping + team_mapping = test_team_mapping() + + # Test 3: Configuration + milb_config = test_configuration() + + # Test 4: Season timing + in_season = test_season_timing() + + # Test 5: Cache manager + cache_ok = test_cache_manager() + + # Final summary + print("\n" + "=" * 60) + print("FINAL DIAGNOSIS") + print("=" * 60) + + issues = [] + + if not api_games: + issues.append("No games found from API") + + if api_errors: + issues.append(f"API errors: {len(api_errors)}") + + if not team_mapping: + issues.append("Team mapping file issues") + + if not milb_config: + issues.append("Configuration file issues") + + if not in_season: + issues.append("Currently outside MiLB season") + + if not cache_ok: + issues.append("Cache manager issues") + + if issues: + print(f"❌ Issues found:") + for issue in issues: + print(f" - {issue}") + else: + print(f"✅ No obvious issues found") + + # Recommendations + print(f"\nRECOMMENDATIONS:") + + if not in_season: + print(f" - MiLB is currently in offseason - no games expected") + print(f" - Consider enabling test_mode in config for testing") + + if not api_games: + print(f" - No games found from API - check API endpoints") + print(f" - Verify sport IDs are correct") + + if api_errors: + print(f" - API errors detected - check network connectivity") + print(f" - Verify API endpoints are accessible") + + print(f"\nTo enable test mode, set 'test_mode': true in config/config.json milb section") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test/test_milb_api.py b/test/test_milb_api.py new file mode 100644 index 00000000..ed393b6d --- /dev/null +++ b/test/test_milb_api.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +""" +Test script to check MiLB API directly +""" + +import requests +import json +from datetime import datetime, timedelta, timezone + +def test_milb_api(): + """Test the MiLB API directly to see what games are available.""" + print("Testing MiLB API directly...") + + # MiLB league sport IDs (same as in the manager) + sport_ids = [10, 11, 12, 13, 14, 15] # Mexican, AAA, AA, A+, A, Rookie + + # Get dates for the next 7 days + now = datetime.now(timezone.utc) + dates = [] + for i in range(-1, 8): # Yesterday + 7 days (same as manager) + date = now + timedelta(days=i) + dates.append(date.strftime("%Y-%m-%d")) + + print(f"Checking dates: {dates}") + print(f"Checking sport IDs: {sport_ids}") + + all_games = {} + + for date in dates: + for sport_id in sport_ids: + try: + url = f"http://statsapi.mlb.com/api/v1/schedule?sportId={sport_id}&date={date}" + print(f"\nFetching MiLB games for sport ID {sport_id}, date: {date}") + print(f"URL: {url}") + + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + } + + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + + data = response.json() + + if not data.get('dates'): + print(f" No dates data for sport ID {sport_id}") + continue + + if not data['dates'][0].get('games'): + print(f" No games found for sport ID {sport_id}") + continue + + games = data['dates'][0]['games'] + print(f" Found {len(games)} games for sport ID {sport_id}") + + for game in games: + game_pk = game['gamePk'] + + home_team_name = game['teams']['home']['team']['name'] + away_team_name = game['teams']['away']['team']['name'] + + home_abbr = game['teams']['home']['team'].get('abbreviation', home_team_name[:3].upper()) + away_abbr = game['teams']['away']['team'].get('abbreviation', away_team_name[:3].upper()) + + status_obj = game['status'] + status_state = status_obj.get('abstractGameState', 'Preview') + detailed_state = status_obj.get('detailedState', '').lower() + + # Map status to consistent format + status_map = { + 'in progress': 'status_in_progress', + 'final': 'status_final', + 'scheduled': 'status_scheduled', + 'preview': 'status_scheduled' + } + mapped_status = status_map.get(detailed_state, 'status_other') + + game_time = datetime.fromisoformat(game['gameDate'].replace('Z', '+00:00')) + + print(f" Game {game_pk}:") + print(f" Teams: {away_abbr} @ {home_abbr}") + print(f" Status: {detailed_state} -> {mapped_status}") + print(f" State: {status_state}") + print(f" Time: {game_time}") + print(f" Scores: {game['teams']['away'].get('score', 0)} - {game['teams']['home'].get('score', 0)}") + + # Check if it's a favorite team (TAM from config) + favorite_teams = ['TAM'] + is_favorite = (home_abbr in favorite_teams or away_abbr in favorite_teams) + if is_favorite: + print(f" ⭐ FAVORITE TEAM GAME") + + # Store game data + game_data = { + 'id': game_pk, + 'away_team': away_abbr, + 'home_team': home_abbr, + 'away_score': game['teams']['away'].get('score', 0), + 'home_score': game['teams']['home'].get('score', 0), + 'status': mapped_status, + 'status_state': status_state, + 'start_time': game['gameDate'], + 'is_favorite': is_favorite + } + + all_games[game_pk] = game_data + + except Exception as e: + print(f"Error fetching MiLB games for sport ID {sport_id}, date {date}: {e}") + + # Summary + print(f"\n{'='*50}") + print(f"SUMMARY:") + print(f"Total games found: {len(all_games)}") + + favorite_games = [g for g in all_games.values() if g['is_favorite']] + print(f"Favorite team games: {len(favorite_games)}") + + live_games = [g for g in all_games.values() if g['status'] == 'status_in_progress'] + print(f"Live games: {len(live_games)}") + + upcoming_games = [g for g in all_games.values() if g['status'] == 'status_scheduled'] + print(f"Upcoming games: {len(upcoming_games)}") + + final_games = [g for g in all_games.values() if g['status'] == 'status_final'] + print(f"Final games: {len(final_games)}") + + if favorite_games: + print(f"\nFavorite team games:") + for game in favorite_games: + print(f" {game['away_team']} @ {game['home_team']} - {game['status']} ({game['status_state']})") + + if live_games: + print(f"\nLive games:") + for game in live_games: + print(f" {game['away_team']} @ {game['home_team']} - {game['away_score']}-{game['home_score']}") + +if __name__ == "__main__": + test_milb_api() \ No newline at end of file