Files
LEDMatrix/test/test_background_service.py
Chuck 9dc1118d79 Feature/background season data (#46)
* Fix NCAAFB ranking display issue

- Remove duplicate ranking system that was drawing rankings behind team logos
- Old system (_get_rank) was drawing rankings at top of logos
- New system (_fetch_team_rankings) correctly draws rankings in bottom corners
- Remove old ranking calls from live, recent, and upcoming game drawing functions
- Remove unnecessary _fetch_rankings() calls from update methods
- Rankings now only appear in designated corner positions, not overlapping logos

Fixes issue where team rankings/betting lines were being drawn behind
team logos instead of replacing team records in the corners.

* Add missing show_ranking and show_records options to NCAAFB web UI

- Add show_ranking option to NCAAFB scoreboard config template
- Add show_records and show_ranking toggle switches to NCAAFB web UI
- Update JavaScript form collection to include new fields
- Users can now control whether to show team records or rankings via web interface

This completes the fix for NCAAFB ranking display - users can now enable
show_ranking in the web UI to see AP Top 25 rankings instead of team records.

* Implement Background Threading for Season Data Fetching

Phase 1: Background Season Data Fetching - COMPLETED

Key Features:
- Created BackgroundDataService class with thread-safe operations
- Implemented automatic retry logic with exponential backoff
- Modified NFL manager to use background service
- Added immediate partial data return for non-blocking display
- Comprehensive logging and statistics tracking

Performance Benefits:
- Main display loop no longer blocked by API calls
- Season data always fresh with background updates
- Better user experience during data fetching

Files Added/Modified:
- src/background_data_service.py (NEW)
- src/nfl_managers.py (updated)
- config/config.template.json (updated)
- test_background_service.py (NEW)
- BACKGROUND_SERVICE_README.md (NEW)

* Fix data validation issues in background service

- Add comprehensive data structure validation in NFL managers
- Handle malformed events gracefully with proper error logging
- Validate cached data format and handle legacy formats
- Add data validation in background service response parsing
- Fix TypeError: string indices must be integers, not 'str'

This fixes the error where events were being treated as strings
instead of dictionaries, causing crashes in recent/upcoming games.

* Phase 2: Apply Background Service to Major Sport Managers

 Applied background service support to:
- NCAAFB Manager (College Football)
- NBA Manager (Basketball)
- NHL Manager (Hockey)
- MLB Manager (Baseball)

🔧 Key Features Added:
- Background service initialization for each sport
- Configurable workers, timeouts, and retry settings
- Graceful fallback when background service is disabled
- Comprehensive logging for monitoring

⚙️ Configuration Updates:
- Added background_service config section to NBA
- Added background_service config section to NHL
- Added background_service config section to NCAAFB
- Each sport can independently enable/disable background service

📈 Performance Benefits:
- Season data fetching no longer blocks display loops
- Immediate response with cached/partial data
- Background threads handle heavy API calls
- Better responsiveness across all supported sports

Next: Apply to remaining managers (MiLB, Soccer, etc.)

* Fix Python compatibility issue in BackgroundDataService shutdown

🐛 Bug Fix:
- Fixed TypeError in ThreadPoolExecutor.shutdown() for older Python versions
- Added try/catch to handle timeout parameter compatibility
- Fallback gracefully for Python < 3.9 that doesn't support timeout parameter

🔧 Technical Details:
- ThreadPoolExecutor.shutdown(timeout=) was added in Python 3.9
- Older versions only support shutdown(wait=)
- Added compatibility layer with proper error handling

 Result:
- No more shutdown exceptions on older Python versions
- Graceful degradation for different Python environments
- Maintains full functionality on newer Python versions

* Phase 2 Complete: Background Service Applied to All Sport Managers

🎉 MAJOR MILESTONE: Complete Background Service Rollout

 All Sport Managers Now Support Background Service:
- MiLB Manager (Minor League Baseball)
- Soccer Manager (Multiple leagues: Premier League, La Liga, etc.)
- Leaderboard Manager (Multi-sport standings)
- Odds Ticker Manager (Live betting odds)

🔧 Technical Implementation:
- Background service initialization in all managers
- Configurable workers, timeouts, and retry settings
- Graceful fallback when background service is disabled
- Comprehensive logging for monitoring and debugging
- Thread-safe operations with proper error handling

⚙️ Configuration Support Added:
- MiLB: background_service config section
- Soccer: background_service config section
- Leaderboard: background_service config section
- Odds Ticker: background_service config section
- Each manager can independently enable/disable background service

📈 Performance Benefits Achieved:
- Non-blocking data fetching across ALL sport managers
- Immediate response with cached/partial data
- Background threads handle heavy API calls
- Significantly improved responsiveness
- Better user experience during data loading

🚀 Production Ready:
- All major sport managers now support background threading
- Comprehensive configuration options
- Robust error handling and fallback mechanisms
- Ready for production deployment

Next: Phase 3 - Advanced features (priority queuing, analytics)

* Update wiki submodule with Background Service documentation

📚 Wiki Documentation Added:
- Complete Background Service Guide with architecture diagrams
- Configuration examples and best practices
- Performance benefits and troubleshooting guide
- Migration guide and advanced features

🔧 Navigation Updates:
- Added to sidebar under Technical section
- Updated home page with performance section
- Highlighted as NEW feature with  icon

The wiki now includes comprehensive documentation for the new
background threading system that improves performance across
all sport managers.

* Fix CacheManager constructor in test script

🐛 Bug Fix:
- Fixed CacheManager initialization in test_background_service.py
- CacheManager no longer takes config_manager parameter
- Updated constructor call to match current implementation

 Result:
- Test script now works with current CacheManager API
- Background service testing can proceed without errors

* Move test_background_service.py to test/ directory

📁 Organization Improvement:
- Moved test_background_service.py from root to test/ directory
- Updated import paths to work from new location
- Fixed sys.path to correctly reference src/ directory
- Updated imports to use relative paths

🔧 Technical Changes:
- Changed sys.path from 'src' to '../src' (go up from test/)
- Updated imports to remove 'src.' prefix
- Maintains all functionality while improving project structure

 Benefits:
- Better project organization
- Test files properly grouped in test/ directory
- Cleaner root directory structure
- Follows standard Python project layout

* Remove old test_background_service.py from root directory

📁 Cleanup:
- Removed test_background_service.py from root directory
- File has been moved to test/ directory for better organization
- Maintains clean project structure

* Fix NCAA FB team ranking display functionality

- Add missing _fetch_team_rankings() calls to all update methods (live, recent, upcoming)
- Add ranking display logic to live manager scorebug layout
- Remove unused old _fetch_rankings() method and top_25_rankings variable
- Rankings now properly display as #X format when show_ranking is enabled
- Fixes non-functional ranking feature despite existing UI and configuration options
2025-09-17 17:25:01 -04:00

184 lines
6.6 KiB
Python

#!/usr/bin/env python3
"""
Test script for Background Data Service with NFL Manager
This script tests the background threading functionality for NFL season data fetching.
It demonstrates how the background service prevents blocking the main display loop.
"""
import os
import sys
import time
import logging
from datetime import datetime
# Add src directory to path (go up one level from test/ to find src/)
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
from background_data_service import BackgroundDataService, get_background_service
from cache_manager import CacheManager
from config_manager import ConfigManager
from nfl_managers import BaseNFLManager
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s.%(msecs)03d - %(levelname)s:%(name)s:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
class MockDisplayManager:
"""Mock display manager for testing."""
def __init__(self):
self.matrix = type('Matrix', (), {'width': 64, 'height': 32})()
self.image = None
def update_display(self):
pass
def format_date_with_ordinal(self, date):
return date.strftime("%B %d")
def test_background_service():
"""Test the background data service functionality."""
logger.info("Starting Background Data Service Test")
# Initialize components
config_manager = ConfigManager()
cache_manager = CacheManager()
# Test configuration for NFL
test_config = {
"nfl_scoreboard": {
"enabled": True,
"test_mode": False,
"background_service": {
"enabled": True,
"max_workers": 2,
"request_timeout": 15,
"max_retries": 2,
"priority": 2
},
"favorite_teams": ["TB", "DAL"],
"display_modes": {
"nfl_live": True,
"nfl_recent": True,
"nfl_upcoming": True
}
},
"timezone": "America/Chicago"
}
# Initialize mock display manager
display_manager = MockDisplayManager()
# Initialize NFL manager
nfl_manager = BaseNFLManager(test_config, display_manager, cache_manager)
logger.info("NFL Manager initialized with background service")
# Test 1: Check if background service is enabled
logger.info(f"Background service enabled: {nfl_manager.background_enabled}")
if nfl_manager.background_service:
logger.info(f"Background service workers: {nfl_manager.background_service.max_workers}")
# Test 2: Test data fetching with background service
logger.info("Testing NFL data fetch with background service...")
start_time = time.time()
# This should start a background fetch and return partial data immediately
data = nfl_manager._fetch_nfl_api_data(use_cache=False)
fetch_time = time.time() - start_time
logger.info(f"Initial fetch completed in {fetch_time:.2f} seconds")
if data and 'events' in data:
logger.info(f"Received {len(data['events'])} events (partial data)")
# Show some sample events
for i, event in enumerate(data['events'][:3]):
logger.info(f" Event {i+1}: {event.get('id', 'N/A')}")
else:
logger.warning("No data received from initial fetch")
# Test 3: Wait for background fetch to complete
logger.info("Waiting for background fetch to complete...")
max_wait_time = 30 # 30 seconds max wait
wait_start = time.time()
while time.time() - wait_start < max_wait_time:
# Check if background fetch is complete
current_year = datetime.now().year
if current_year in nfl_manager.background_fetch_requests:
request_id = nfl_manager.background_fetch_requests[current_year]
result = nfl_manager.background_service.get_result(request_id)
if result and result.success:
logger.info(f"Background fetch completed successfully in {result.fetch_time:.2f}s")
logger.info(f"Full dataset contains {len(result.data)} events")
break
elif result and not result.success:
logger.error(f"Background fetch failed: {result.error}")
break
else:
# Check if we have cached data now
cached_data = cache_manager.get(f"nfl_schedule_{current_year}")
if cached_data:
logger.info(f"Found cached data with {len(cached_data)} events")
break
time.sleep(1)
logger.info("Still waiting for background fetch...")
# Test 4: Test subsequent fetch (should use cache)
logger.info("Testing subsequent fetch (should use cache)...")
start_time = time.time()
data2 = nfl_manager._fetch_nfl_api_data(use_cache=True)
fetch_time2 = time.time() - start_time
logger.info(f"Subsequent fetch completed in {fetch_time2:.2f} seconds")
if data2 and 'events' in data2:
logger.info(f"Received {len(data2['events'])} events from cache")
# Test 5: Show service statistics
if nfl_manager.background_service:
stats = nfl_manager.background_service.get_statistics()
logger.info("Background Service Statistics:")
for key, value in stats.items():
logger.info(f" {key}: {value}")
# Test 6: Test with background service disabled
logger.info("Testing with background service disabled...")
test_config_disabled = test_config.copy()
test_config_disabled["nfl_scoreboard"]["background_service"]["enabled"] = False
nfl_manager_disabled = BaseNFLManager(test_config_disabled, display_manager, cache_manager)
logger.info(f"Background service enabled: {nfl_manager_disabled.background_enabled}")
start_time = time.time()
data3 = nfl_manager_disabled._fetch_nfl_api_data(use_cache=False)
fetch_time3 = time.time() - start_time
logger.info(f"Synchronous fetch completed in {fetch_time3:.2f} seconds")
if data3 and 'events' in data3:
logger.info(f"Received {len(data3['events'])} events synchronously")
logger.info("Background Data Service Test Complete!")
# Cleanup
if nfl_manager.background_service:
nfl_manager.background_service.shutdown(wait=True, timeout=10)
if __name__ == "__main__":
try:
test_background_service()
except KeyboardInterrupt:
logger.info("Test interrupted by user")
except Exception as e:
logger.error(f"Test failed with error: {e}", exc_info=True)