mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
add favorite team filtering to soccer manager and ensure timezones are respected
This commit is contained in:
@@ -95,12 +95,25 @@ class BaseSoccerManager:
|
|||||||
self.logger.info(f"Team map file: {self.team_map_file}")
|
self.logger.info(f"Team map file: {self.team_map_file}")
|
||||||
self.logger.info(f"Team map update interval: {self.team_map_update_days} days")
|
self.logger.info(f"Team map update interval: {self.team_map_update_days} days")
|
||||||
|
|
||||||
self.config_manager = ConfigManager(config)
|
# Log favorite teams configuration
|
||||||
|
show_favorites_only = self.soccer_config.get("show_favorite_teams_only", False)
|
||||||
|
if show_favorites_only:
|
||||||
|
self.logger.info(f"Favorite teams filtering enabled. Favorite teams: {self.favorite_teams}")
|
||||||
|
else:
|
||||||
|
self.logger.info("Favorite teams filtering disabled. Showing all teams.")
|
||||||
|
|
||||||
|
self.config_manager = ConfigManager()
|
||||||
|
|
||||||
def _get_timezone(self):
|
def _get_timezone(self):
|
||||||
try:
|
try:
|
||||||
return pytz.timezone(self.config_manager.get_timezone())
|
timezone_str = self.config_manager.get_timezone()
|
||||||
|
self.logger.debug(f"[Soccer] Config timezone: {timezone_str}")
|
||||||
|
return pytz.timezone(timezone_str)
|
||||||
except pytz.UnknownTimeZoneError:
|
except pytz.UnknownTimeZoneError:
|
||||||
|
self.logger.warning(f"[Soccer] Unknown timezone: {timezone_str}, falling back to UTC")
|
||||||
|
return pytz.utc
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"[Soccer] Error getting timezone: {e}, falling back to UTC")
|
||||||
return pytz.utc
|
return pytz.utc
|
||||||
|
|
||||||
def _fetch_odds(self, game: Dict) -> None:
|
def _fetch_odds(self, game: Dict) -> None:
|
||||||
@@ -327,7 +340,7 @@ class BaseSoccerManager:
|
|||||||
if team_abbrev in self._logo_cache:
|
if team_abbrev in self._logo_cache:
|
||||||
return self._logo_cache[team_abbrev]
|
return self._logo_cache[team_abbrev]
|
||||||
|
|
||||||
# Try to find the logo file with case-insensitive matching
|
# Try to find the logo file with case-insensitive matching and common variations
|
||||||
logo_path = None
|
logo_path = None
|
||||||
expected_path = os.path.join(self.logo_dir, f"{team_abbrev}.png")
|
expected_path = os.path.join(self.logo_dir, f"{team_abbrev}.png")
|
||||||
|
|
||||||
@@ -335,13 +348,38 @@ class BaseSoccerManager:
|
|||||||
if os.path.exists(expected_path):
|
if os.path.exists(expected_path):
|
||||||
logo_path = expected_path
|
logo_path = expected_path
|
||||||
else:
|
else:
|
||||||
# Try case-insensitive matching
|
# Try case-insensitive matching and common variations
|
||||||
try:
|
try:
|
||||||
for filename in os.listdir(self.logo_dir):
|
for filename in os.listdir(self.logo_dir):
|
||||||
if filename.lower() == f"{team_abbrev.lower()}.png":
|
filename_lower = filename.lower()
|
||||||
|
team_abbrev_lower = team_abbrev.lower()
|
||||||
|
|
||||||
|
# Exact case-insensitive match
|
||||||
|
if filename_lower == f"{team_abbrev_lower}.png":
|
||||||
logo_path = os.path.join(self.logo_dir, filename)
|
logo_path = os.path.join(self.logo_dir, filename)
|
||||||
self.logger.debug(f"Found case-insensitive match: {filename} for {team_abbrev}")
|
self.logger.debug(f"Found case-insensitive match: {filename} for {team_abbrev}")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Handle common team abbreviation variations
|
||||||
|
if team_abbrev == "MTL":
|
||||||
|
# Montreal variations
|
||||||
|
if filename_lower in ["cf_montral.png", "mon.png", "montreal.png"]:
|
||||||
|
logo_path = os.path.join(self.logo_dir, filename)
|
||||||
|
self.logger.debug(f"Found Montreal variation: {filename} for {team_abbrev}")
|
||||||
|
break
|
||||||
|
elif team_abbrev == "LAFC":
|
||||||
|
# LAFC variations
|
||||||
|
if filename_lower in ["lafc.png", "la_fc.png"]:
|
||||||
|
logo_path = os.path.join(self.logo_dir, filename)
|
||||||
|
self.logger.debug(f"Found LAFC variation: {filename} for {team_abbrev}")
|
||||||
|
break
|
||||||
|
elif team_abbrev == "NY":
|
||||||
|
# New York variations
|
||||||
|
if filename_lower in ["ny.png", "nycfc.png", "nyrb.png"]:
|
||||||
|
logo_path = os.path.join(self.logo_dir, filename)
|
||||||
|
self.logger.debug(f"Found NY variation: {filename} for {team_abbrev}")
|
||||||
|
break
|
||||||
|
|
||||||
except (OSError, PermissionError) as e:
|
except (OSError, PermissionError) as e:
|
||||||
self.logger.warning(f"Error listing directory {self.logo_dir}: {e}")
|
self.logger.warning(f"Error listing directory {self.logo_dir}: {e}")
|
||||||
|
|
||||||
@@ -470,6 +508,7 @@ class BaseSoccerManager:
|
|||||||
if start_time_utc:
|
if start_time_utc:
|
||||||
local_time = start_time_utc.astimezone(self._get_timezone())
|
local_time = start_time_utc.astimezone(self._get_timezone())
|
||||||
game_time = local_time.strftime("%I:%M%p").lower().lstrip('0') # e.g., 2:30pm
|
game_time = local_time.strftime("%I:%M%p").lower().lstrip('0') # e.g., 2:30pm
|
||||||
|
self.logger.debug(f"[Soccer] Timezone conversion - UTC: {start_time_utc}, Local: {local_time}, Formatted: {game_time}")
|
||||||
|
|
||||||
# Check date format from config
|
# Check date format from config
|
||||||
use_short_date_format = self.config.get('display', {}).get('use_short_date_format', False)
|
use_short_date_format = self.config.get('display', {}).get('use_short_date_format', False)
|
||||||
@@ -759,22 +798,28 @@ class SoccerLiveManager(BaseSoccerManager):
|
|||||||
if data and "events" in data:
|
if data and "events" in data:
|
||||||
for event in data["events"]:
|
for event in data["events"]:
|
||||||
details = self._extract_game_details(event)
|
details = self._extract_game_details(event)
|
||||||
# Ensure it's live and involves a favorite team (if specified)
|
# Ensure it's live
|
||||||
if details and details["is_live"]:
|
if details and details["is_live"]:
|
||||||
self._fetch_odds(details)
|
self._fetch_odds(details)
|
||||||
new_live_games.append(details)
|
new_live_games.append(details)
|
||||||
|
|
||||||
|
# Filter for favorite teams only if the config is set
|
||||||
|
if self.soccer_config.get("show_favorite_teams_only", False) and self.favorite_teams:
|
||||||
|
new_live_games = [game for game in new_live_games if game['home_abbr'] in self.favorite_teams or game['away_abbr'] in self.favorite_teams]
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
should_log = (current_time - self.last_log_time >= self.log_interval or
|
should_log = (current_time - self.last_log_time >= self.log_interval or
|
||||||
len(new_live_games) != len(self.live_games) or
|
len(new_live_games) != len(self.live_games) or
|
||||||
not self.live_games)
|
not self.live_games)
|
||||||
if should_log:
|
if should_log:
|
||||||
if new_live_games:
|
if new_live_games:
|
||||||
self.logger.info(f"[Soccer] Found {len(new_live_games)} live games involving favorite teams / all teams.")
|
filter_text = "favorite teams" if self.soccer_config.get("show_favorite_teams_only", False) and self.favorite_teams else "all teams"
|
||||||
|
self.logger.info(f"[Soccer] Found {len(new_live_games)} live games involving {filter_text}.")
|
||||||
for game in new_live_games:
|
for game in new_live_games:
|
||||||
self.logger.info(f"[Soccer] Live game: {game['away_abbr']} vs {game['home_abbr']} ({game['game_clock_display']}) - {game['league']}")
|
self.logger.info(f"[Soccer] Live game: {game['away_abbr']} vs {game['home_abbr']} ({game['game_clock_display']}) - {game['league']}")
|
||||||
else:
|
else:
|
||||||
self.logger.info("[Soccer] No live games found matching criteria.")
|
filter_text = "favorite teams" if self.soccer_config.get("show_favorite_teams_only", False) and self.favorite_teams else "criteria"
|
||||||
|
self.logger.info(f"[Soccer] No live games found matching {filter_text}.")
|
||||||
self.last_log_time = current_time
|
self.last_log_time = current_time
|
||||||
|
|
||||||
# Update game list and current game
|
# Update game list and current game
|
||||||
@@ -825,7 +870,8 @@ class SoccerLiveManager(BaseSoccerManager):
|
|||||||
else:
|
else:
|
||||||
# No live games found
|
# No live games found
|
||||||
if self.live_games: # Log only if previously had games
|
if self.live_games: # Log only if previously had games
|
||||||
self.logger.info("[Soccer] All live games have ended or no longer match criteria.")
|
filter_text = "favorite teams" if self.soccer_config.get("show_favorite_teams_only", False) and self.favorite_teams else "criteria"
|
||||||
|
self.logger.info(f"[Soccer] All live games have ended or no longer match {filter_text}.")
|
||||||
self.live_games = []
|
self.live_games = []
|
||||||
self.current_game = None
|
self.current_game = None
|
||||||
|
|
||||||
|
|||||||
194
test/test_soccer_favorite_teams.py
Normal file
194
test/test_soccer_favorite_teams.py
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script to verify soccer manager favorite teams filtering functionality.
|
||||||
|
This test checks that when show_favorite_teams_only is enabled, only games
|
||||||
|
involving favorite teams are processed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
# Add the src directory to the path so we can import the soccer managers
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||||
|
|
||||||
|
from soccer_managers import BaseSoccerManager
|
||||||
|
from display_manager import DisplayManager
|
||||||
|
from cache_manager import CacheManager
|
||||||
|
|
||||||
|
def create_test_config(show_favorite_teams_only=True, favorite_teams=None):
|
||||||
|
"""Create a test configuration for soccer manager."""
|
||||||
|
if favorite_teams is None:
|
||||||
|
favorite_teams = ["DAL", "TB"]
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"soccer_scoreboard": {
|
||||||
|
"enabled": True,
|
||||||
|
"show_favorite_teams_only": show_favorite_teams_only,
|
||||||
|
"favorite_teams": favorite_teams,
|
||||||
|
"leagues": ["usa.1"],
|
||||||
|
"logo_dir": "assets/sports/soccer_logos",
|
||||||
|
"recent_game_hours": 168,
|
||||||
|
"update_interval_seconds": 3600
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"hardware": {
|
||||||
|
"rows": 32,
|
||||||
|
"cols": 64,
|
||||||
|
"chain_length": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timezone": "America/Chicago"
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
|
||||||
|
def create_test_game_data():
|
||||||
|
"""Create test game data with various teams."""
|
||||||
|
now = datetime.now(pytz.utc)
|
||||||
|
|
||||||
|
games = [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"date": now.isoformat(),
|
||||||
|
"competitions": [{
|
||||||
|
"status": {
|
||||||
|
"type": {"name": "STATUS_IN_PROGRESS", "shortDetail": "45'"}
|
||||||
|
},
|
||||||
|
"competitors": [
|
||||||
|
{
|
||||||
|
"homeAway": "home",
|
||||||
|
"team": {"abbreviation": "DAL"},
|
||||||
|
"score": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"homeAway": "away",
|
||||||
|
"team": {"abbreviation": "LAFC"},
|
||||||
|
"score": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
"league": {"slug": "usa.1", "name": "MLS"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"date": now.isoformat(),
|
||||||
|
"competitions": [{
|
||||||
|
"status": {
|
||||||
|
"type": {"name": "STATUS_IN_PROGRESS", "shortDetail": "30'"}
|
||||||
|
},
|
||||||
|
"competitors": [
|
||||||
|
{
|
||||||
|
"homeAway": "home",
|
||||||
|
"team": {"abbreviation": "TB"},
|
||||||
|
"score": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"homeAway": "away",
|
||||||
|
"team": {"abbreviation": "NY"},
|
||||||
|
"score": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
"league": {"slug": "usa.1", "name": "MLS"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"date": now.isoformat(),
|
||||||
|
"competitions": [{
|
||||||
|
"status": {
|
||||||
|
"type": {"name": "STATUS_IN_PROGRESS", "shortDetail": "15'"}
|
||||||
|
},
|
||||||
|
"competitors": [
|
||||||
|
{
|
||||||
|
"homeAway": "home",
|
||||||
|
"team": {"abbreviation": "LAFC"},
|
||||||
|
"score": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"homeAway": "away",
|
||||||
|
"team": {"abbreviation": "NY"},
|
||||||
|
"score": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
"league": {"slug": "usa.1", "name": "MLS"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return games
|
||||||
|
|
||||||
|
def test_favorite_teams_filtering():
|
||||||
|
"""Test that favorite teams filtering works correctly."""
|
||||||
|
print("Testing soccer manager favorite teams filtering...")
|
||||||
|
|
||||||
|
# Test 1: With favorite teams filtering enabled
|
||||||
|
print("\n1. Testing with show_favorite_teams_only=True")
|
||||||
|
config = create_test_config(show_favorite_teams_only=True, favorite_teams=["DAL", "TB"])
|
||||||
|
|
||||||
|
# Create mock display and cache managers
|
||||||
|
display_manager = DisplayManager(config)
|
||||||
|
cache_manager = CacheManager()
|
||||||
|
|
||||||
|
# Create soccer manager
|
||||||
|
soccer_manager = BaseSoccerManager(config, display_manager, cache_manager)
|
||||||
|
|
||||||
|
# Create test game data
|
||||||
|
test_games = create_test_game_data()
|
||||||
|
|
||||||
|
# Process games and check filtering
|
||||||
|
filtered_games = []
|
||||||
|
for game_event in test_games:
|
||||||
|
details = soccer_manager._extract_game_details(game_event)
|
||||||
|
if details and details["is_live"]:
|
||||||
|
filtered_games.append(details)
|
||||||
|
|
||||||
|
# Apply favorite teams filtering
|
||||||
|
if soccer_manager.soccer_config.get("show_favorite_teams_only", False) and soccer_manager.favorite_teams:
|
||||||
|
filtered_games = [game for game in filtered_games if game['home_abbr'] in soccer_manager.favorite_teams or game['away_abbr'] in soccer_manager.favorite_teams]
|
||||||
|
|
||||||
|
print(f" Total games: {len(test_games)}")
|
||||||
|
print(f" Live games: {len([g for g in test_games if g['competitions'][0]['status']['type']['name'] == 'STATUS_IN_PROGRESS'])}")
|
||||||
|
print(f" Games after favorite teams filtering: {len(filtered_games)}")
|
||||||
|
|
||||||
|
# Verify only games with DAL or TB are included
|
||||||
|
expected_teams = {"DAL", "TB"}
|
||||||
|
for game in filtered_games:
|
||||||
|
home_team = game['home_abbr']
|
||||||
|
away_team = game['away_abbr']
|
||||||
|
assert home_team in expected_teams or away_team in expected_teams, f"Game {home_team} vs {away_team} should not be included"
|
||||||
|
print(f" ✓ Included: {away_team} vs {home_team}")
|
||||||
|
|
||||||
|
# Test 2: With favorite teams filtering disabled
|
||||||
|
print("\n2. Testing with show_favorite_teams_only=False")
|
||||||
|
config = create_test_config(show_favorite_teams_only=False, favorite_teams=["DAL", "TB"])
|
||||||
|
soccer_manager = BaseSoccerManager(config, display_manager, cache_manager)
|
||||||
|
|
||||||
|
filtered_games = []
|
||||||
|
for game_event in test_games:
|
||||||
|
details = soccer_manager._extract_game_details(game_event)
|
||||||
|
if details and details["is_live"]:
|
||||||
|
filtered_games.append(details)
|
||||||
|
|
||||||
|
# Apply favorite teams filtering (should not filter when disabled)
|
||||||
|
if soccer_manager.soccer_config.get("show_favorite_teams_only", False) and soccer_manager.favorite_teams:
|
||||||
|
filtered_games = [game for game in filtered_games if game['home_abbr'] in soccer_manager.favorite_teams or game['away_abbr'] in soccer_manager.favorite_teams]
|
||||||
|
|
||||||
|
print(f" Total games: {len(test_games)}")
|
||||||
|
print(f" Live games: {len([g for g in test_games if g['competitions'][0]['status']['type']['name'] == 'STATUS_IN_PROGRESS'])}")
|
||||||
|
print(f" Games after filtering (should be all live games): {len(filtered_games)}")
|
||||||
|
|
||||||
|
# Verify all live games are included when filtering is disabled
|
||||||
|
assert len(filtered_games) == 3, f"Expected 3 games, got {len(filtered_games)}"
|
||||||
|
print(" ✓ All live games included when filtering is disabled")
|
||||||
|
|
||||||
|
print("\n✅ All tests passed! Favorite teams filtering is working correctly.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
test_favorite_teams_filtering()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Test failed: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit(1)
|
||||||
Reference in New Issue
Block a user