mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
Fix odds ticker dynamic duration calculation
- Fixed double-counting of display width in total_scroll_width calculation - Added detailed debug logging for dynamic duration calculation - Added debug logging for scrolling behavior and loop resets - Created test script for debugging dynamic duration issues - The issue was that total_scroll_width included display width, causing incorrect duration calculations that resulted in early cutoff
This commit is contained in:
@@ -12,6 +12,14 @@ from src.cache_manager import CacheManager
|
|||||||
from src.config_manager import ConfigManager
|
from src.config_manager import ConfigManager
|
||||||
from src.odds_manager import OddsManager
|
from src.odds_manager import OddsManager
|
||||||
|
|
||||||
|
# Import the API counter function from web interface
|
||||||
|
try:
|
||||||
|
from web_interface_v2 import increment_api_counter
|
||||||
|
except ImportError:
|
||||||
|
# Fallback if web interface is not available
|
||||||
|
def increment_api_counter(kind: str, count: int = 1):
|
||||||
|
pass
|
||||||
|
|
||||||
# Get logger
|
# Get logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -214,6 +222,9 @@ class OddsTickerManager:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
|
# Increment API counter for sports data
|
||||||
|
increment_api_counter('sports', 1)
|
||||||
|
|
||||||
# Different path for college sports records
|
# Different path for college sports records
|
||||||
if league == 'college-football':
|
if league == 'college-football':
|
||||||
record_items = data.get('team', {}).get('record', {}).get('items', [])
|
record_items = data.get('team', {}).get('record', {}).get('items', [])
|
||||||
@@ -371,6 +382,10 @@ class OddsTickerManager:
|
|||||||
response = requests.get(url, timeout=self.request_timeout)
|
response = requests.get(url, timeout=self.request_timeout)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
|
# Increment API counter for sports data
|
||||||
|
increment_api_counter('sports', 1)
|
||||||
|
|
||||||
self.cache_manager.set(cache_key, data)
|
self.cache_manager.set(cache_key, data)
|
||||||
logger.debug(f"Cached scoreboard for {league} on {date} with a TTL of {ttl} seconds.")
|
logger.debug(f"Cached scoreboard for {league} on {date} with a TTL of {ttl} seconds.")
|
||||||
else:
|
else:
|
||||||
@@ -1140,7 +1155,8 @@ class OddsTickerManager:
|
|||||||
|
|
||||||
gap_width = 24 # Reduced gap between games
|
gap_width = 24 # Reduced gap between games
|
||||||
display_width = self.display_manager.matrix.width # Add display width of black space at start
|
display_width = self.display_manager.matrix.width # Add display width of black space at start
|
||||||
total_width = display_width + sum(img.width for img in game_images) + gap_width * (len(game_images))
|
content_width = sum(img.width for img in game_images) + gap_width * (len(game_images))
|
||||||
|
total_width = display_width + content_width
|
||||||
height = self.display_manager.matrix.height
|
height = self.display_manager.matrix.height
|
||||||
|
|
||||||
self.ticker_image = Image.new('RGB', (total_width, height), color=(0, 0, 0))
|
self.ticker_image = Image.new('RGB', (total_width, height), color=(0, 0, 0))
|
||||||
@@ -1156,8 +1172,14 @@ class OddsTickerManager:
|
|||||||
self.ticker_image.putpixel((bar_x, y), (255, 255, 255))
|
self.ticker_image.putpixel((bar_x, y), (255, 255, 255))
|
||||||
current_x += gap_width
|
current_x += gap_width
|
||||||
|
|
||||||
# Calculate total scroll width for dynamic duration
|
# Calculate total scroll width for dynamic duration (only the content width, not including display width)
|
||||||
self.total_scroll_width = total_width
|
self.total_scroll_width = content_width
|
||||||
|
logger.debug(f"Odds ticker image creation:")
|
||||||
|
logger.debug(f" Display width: {display_width}px")
|
||||||
|
logger.debug(f" Content width: {content_width}px")
|
||||||
|
logger.debug(f" Total image width: {total_width}px")
|
||||||
|
logger.debug(f" Number of games: {len(game_images)}")
|
||||||
|
logger.debug(f" Gap width: {gap_width}px")
|
||||||
self.calculate_dynamic_duration()
|
self.calculate_dynamic_duration()
|
||||||
|
|
||||||
def _draw_text_with_outline(self, draw: ImageDraw.Draw, text: str, position: tuple, font: ImageFont.FreeTypeFont,
|
def _draw_text_with_outline(self, draw: ImageDraw.Draw, text: str, position: tuple, font: ImageFont.FreeTypeFont,
|
||||||
@@ -1278,6 +1300,9 @@ class OddsTickerManager:
|
|||||||
"""Display the odds ticker."""
|
"""Display the odds ticker."""
|
||||||
logger.debug("Entering display method")
|
logger.debug("Entering display method")
|
||||||
logger.debug(f"Odds ticker enabled: {self.is_enabled}")
|
logger.debug(f"Odds ticker enabled: {self.is_enabled}")
|
||||||
|
logger.debug(f"Current scroll position: {self.scroll_position}")
|
||||||
|
logger.debug(f"Ticker image width: {self.ticker_image.width if self.ticker_image else 'None'}")
|
||||||
|
logger.debug(f"Dynamic duration: {self.dynamic_duration}s")
|
||||||
|
|
||||||
if not self.is_enabled:
|
if not self.is_enabled:
|
||||||
logger.debug("Odds ticker is disabled, exiting display method.")
|
logger.debug("Odds ticker is disabled, exiting display method.")
|
||||||
@@ -1326,10 +1351,12 @@ class OddsTickerManager:
|
|||||||
if self.loop:
|
if self.loop:
|
||||||
# Reset position when we've scrolled past the end for a continuous loop
|
# Reset position when we've scrolled past the end for a continuous loop
|
||||||
if self.scroll_position >= self.ticker_image.width:
|
if self.scroll_position >= self.ticker_image.width:
|
||||||
|
logger.debug(f"Odds ticker loop reset: scroll_position {self.scroll_position} >= image width {self.ticker_image.width}")
|
||||||
self.scroll_position = 0
|
self.scroll_position = 0
|
||||||
else:
|
else:
|
||||||
# Stop scrolling when we reach the end
|
# Stop scrolling when we reach the end
|
||||||
if self.scroll_position >= self.ticker_image.width - width:
|
if self.scroll_position >= self.ticker_image.width - width:
|
||||||
|
logger.debug(f"Odds ticker reached end: scroll_position {self.scroll_position} >= {self.ticker_image.width - width}")
|
||||||
self.scroll_position = self.ticker_image.width - width
|
self.scroll_position = self.ticker_image.width - width
|
||||||
# Signal that scrolling has stopped
|
# Signal that scrolling has stopped
|
||||||
self.display_manager.set_scrolling_state(False)
|
self.display_manager.set_scrolling_state(False)
|
||||||
|
|||||||
122
test/test_odds_ticker_dynamic_duration.py
Normal file
122
test/test_odds_ticker_dynamic_duration.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script for debugging OddsTickerManager dynamic duration calculation
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Add the parent directory to the Python path so we can import from src
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
|
||||||
|
from src.display_manager import DisplayManager
|
||||||
|
from src.config_manager import ConfigManager
|
||||||
|
from src.odds_ticker_manager import OddsTickerManager
|
||||||
|
|
||||||
|
# Configure logging to show debug information
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='%(asctime)s.%(msecs)03d - %(levelname)s:%(name)s:%(message)s',
|
||||||
|
datefmt='%H:%M:%S'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_dynamic_duration():
|
||||||
|
"""Test the dynamic duration calculation for odds ticker."""
|
||||||
|
print("Testing OddsTickerManager Dynamic Duration...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Load configuration
|
||||||
|
config_manager = ConfigManager()
|
||||||
|
config = config_manager.load_config()
|
||||||
|
|
||||||
|
# Initialize display manager
|
||||||
|
display_manager = DisplayManager(config)
|
||||||
|
|
||||||
|
# Initialize odds ticker
|
||||||
|
odds_ticker = OddsTickerManager(config, display_manager)
|
||||||
|
|
||||||
|
print(f"Odds ticker enabled: {odds_ticker.is_enabled}")
|
||||||
|
print(f"Dynamic duration enabled: {odds_ticker.dynamic_duration_enabled}")
|
||||||
|
print(f"Min duration: {odds_ticker.min_duration}s")
|
||||||
|
print(f"Max duration: {odds_ticker.max_duration}s")
|
||||||
|
print(f"Duration buffer: {odds_ticker.duration_buffer}")
|
||||||
|
print(f"Scroll speed: {odds_ticker.scroll_speed}")
|
||||||
|
print(f"Scroll delay: {odds_ticker.scroll_delay}")
|
||||||
|
print(f"Display width: {display_manager.matrix.width}")
|
||||||
|
|
||||||
|
if not odds_ticker.is_enabled:
|
||||||
|
print("Odds ticker is disabled in config. Enabling for test...")
|
||||||
|
odds_ticker.is_enabled = True
|
||||||
|
|
||||||
|
# Temporarily disable favorite teams filter for testing
|
||||||
|
print("Temporarily disabling favorite teams filter to test display...")
|
||||||
|
original_show_favorite = odds_ticker.show_favorite_teams_only
|
||||||
|
odds_ticker.show_favorite_teams_only = False
|
||||||
|
|
||||||
|
# Update odds ticker data
|
||||||
|
print("\nUpdating odds ticker data...")
|
||||||
|
odds_ticker.update()
|
||||||
|
|
||||||
|
print(f"Found {len(odds_ticker.games_data)} games")
|
||||||
|
|
||||||
|
if odds_ticker.games_data:
|
||||||
|
print("\nSample game data:")
|
||||||
|
for i, game in enumerate(odds_ticker.games_data[:3]): # Show first 3 games
|
||||||
|
print(f" Game {i+1}: {game['away_team']} @ {game['home_team']}")
|
||||||
|
print(f" Time: {game['start_time']}")
|
||||||
|
print(f" League: {game['league']}")
|
||||||
|
if game.get('odds'):
|
||||||
|
print(f" Has odds: Yes")
|
||||||
|
else:
|
||||||
|
print(f" Has odds: No")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Check dynamic duration calculation
|
||||||
|
print("\nDynamic Duration Analysis:")
|
||||||
|
print(f"Total scroll width: {odds_ticker.total_scroll_width}px")
|
||||||
|
print(f"Calculated dynamic duration: {odds_ticker.dynamic_duration}s")
|
||||||
|
|
||||||
|
# Calculate expected duration manually
|
||||||
|
display_width = display_manager.matrix.width
|
||||||
|
total_scroll_distance = display_width + odds_ticker.total_scroll_width
|
||||||
|
frames_needed = total_scroll_distance / odds_ticker.scroll_speed
|
||||||
|
total_time = frames_needed * odds_ticker.scroll_delay
|
||||||
|
buffer_time = total_time * odds_ticker.duration_buffer
|
||||||
|
calculated_duration = int(total_time + buffer_time)
|
||||||
|
|
||||||
|
print(f"\nManual calculation:")
|
||||||
|
print(f" Display width: {display_width}px")
|
||||||
|
print(f" Content width: {odds_ticker.total_scroll_width}px")
|
||||||
|
print(f" Total scroll distance: {total_scroll_distance}px")
|
||||||
|
print(f" Frames needed: {frames_needed:.1f}")
|
||||||
|
print(f" Base time: {total_time:.2f}s")
|
||||||
|
print(f" Buffer time: {buffer_time:.2f}s ({odds_ticker.duration_buffer*100}%)")
|
||||||
|
print(f" Calculated duration: {calculated_duration}s")
|
||||||
|
|
||||||
|
# Test display for a few iterations
|
||||||
|
print(f"\nTesting display for 10 iterations...")
|
||||||
|
for i in range(10):
|
||||||
|
print(f" Display iteration {i+1} starting...")
|
||||||
|
odds_ticker.display()
|
||||||
|
print(f" Display iteration {i+1} complete - scroll position: {odds_ticker.scroll_position}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("No games found even with favorite teams filter disabled.")
|
||||||
|
|
||||||
|
# Restore original setting
|
||||||
|
odds_ticker.show_favorite_teams_only = original_show_favorite
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
display_manager.cleanup()
|
||||||
|
print("\nTest completed successfully!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during test: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_dynamic_duration()
|
||||||
Reference in New Issue
Block a user