"""Database-aware alias loaders for team and stadium resolution. These loaders check the Django TeamAlias and StadiumAlias models in addition to the hardcoded mappings, allowing aliases to be managed via the admin interface. """ from datetime import date from typing import Optional class DatabaseTeamAliasLoader: """Load team aliases from the Django database. Checks the core.TeamAlias model for alias mappings, supporting date-aware lookups for historical names. """ def resolve( self, value: str, sport_code: str, check_date: Optional[date] = None, ) -> Optional[str]: """Resolve an alias value to a canonical team ID. Args: value: Alias value to look up (case-insensitive) sport_code: Sport code to filter by check_date: Date to check validity (None = current date) Returns: Canonical team ID if found, None otherwise """ from core.models import TeamAlias from django.db.models import Q if check_date is None: check_date = date.today() value_lower = value.lower().strip() # Query aliases matching the value and sport aliases = TeamAlias.objects.filter( alias__iexact=value_lower, team__sport__code=sport_code, ).select_related('team') for alias in aliases: if alias.is_valid_for_date(check_date): return alias.team.id return None def get_aliases_for_team( self, team_id: str, check_date: Optional[date] = None, ) -> list: """Get all aliases for a team. Args: team_id: Team ID check_date: Date to filter by (None = all aliases) Returns: List of TeamAlias objects """ from core.models import TeamAlias aliases = TeamAlias.objects.filter(team_id=team_id) if check_date: result = [] for alias in aliases: if alias.is_valid_for_date(check_date): result.append(alias) return result return list(aliases) class DatabaseStadiumAliasLoader: """Load stadium aliases from the Django database. Checks the core.StadiumAlias model for alias mappings, supporting date-aware lookups for naming rights changes. """ def resolve( self, name: str, sport_code: str, check_date: Optional[date] = None, ) -> Optional[str]: """Resolve a stadium name to a canonical stadium ID. Args: name: Stadium name to look up (case-insensitive) sport_code: Sport code to filter by check_date: Date to check validity (None = current date) Returns: Canonical stadium ID if found, None otherwise """ from core.models import StadiumAlias if check_date is None: check_date = date.today() name_lower = name.lower().strip() # Query aliases matching the name and sport aliases = StadiumAlias.objects.filter( alias__iexact=name_lower, stadium__sport__code=sport_code, ).select_related('stadium') for alias in aliases: if alias.is_valid_for_date(check_date): return alias.stadium.id return None # Global instances _db_team_loader: Optional[DatabaseTeamAliasLoader] = None _db_stadium_loader: Optional[DatabaseStadiumAliasLoader] = None def get_db_team_alias_loader() -> DatabaseTeamAliasLoader: """Get the database team alias loader.""" global _db_team_loader if _db_team_loader is None: _db_team_loader = DatabaseTeamAliasLoader() return _db_team_loader def get_db_stadium_alias_loader() -> DatabaseStadiumAliasLoader: """Get the database stadium alias loader.""" global _db_stadium_loader if _db_stadium_loader is None: _db_stadium_loader = DatabaseStadiumAliasLoader() return _db_stadium_loader