diff --git a/.vscode/settings.json b/.vscode/settings.json index 551e8c6..1d5498f 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { "python.pythonPath": "/bin/python3", - "workbench.colorTheme": "Atom One Dark" + "workbench.colorTheme": "GitHub Dark" } \ No newline at end of file diff --git a/lib/config/ohmyoled.conf b/lib/config/ohmyoled.conf index e92e4ad..ee3c77d 100644 --- a/lib/config/ohmyoled.conf +++ b/lib/config/ohmyoled.conf @@ -42,14 +42,9 @@ Run=False [sport] Run=False -# football -# baseball -# hockey -# basketball -# formula 1 -# sport= -# For Team IDs Baseball https://dashboard.api-football.com/baseball/ids/teams -# For Team IDs Basketball https://dashboard.api-football.com/basketball/ids/teams/USA -# Only Support USA -team_id = 6 +# sportsipy +# api-sports +api=api-sports +sport=basketball +team_id = # optional Will pull all data for all unless specified \ No newline at end of file diff --git a/lib/run.py b/lib/run.py index fa5a450..07af8bb 100755 --- a/lib/run.py +++ b/lib/run.py @@ -57,10 +57,10 @@ def __init__(self, config): super().__init__() self.config = config self.runner_logger = logger - if self.config['basic'].getboolean('testing'): + if not self.config['basic'].getboolean('testing'): self.logger.setLevel(self.config['basic'].getint('loglevel')) else: - self.logger.setLevel(10) + self.logger.setLevel(logging.DEBUG) self.logger.debug(f"Runner logger is set to {self.logger.getEffectiveLevel()}") def run_non_async_request(self, url) -> Response: diff --git a/lib/sports/apisports/baseball/baseball.py b/lib/sports/apisports/baseball/baseball.py index 0386edc..fdb745f 100755 --- a/lib/sports/apisports/baseball/baseball.py +++ b/lib/sports/apisports/baseball/baseball.py @@ -1,6 +1,9 @@ from lib.run import Runner from datetime import datetime from lib.sports.apisports.result import SportApiResult +from lib.sports.logo import logo_map, Logo +from datetime import datetime +import lib.sports.sportbase as base class Baseball(Runner): def __init__(self, token, config, headers): @@ -20,19 +23,79 @@ def parse_args(self): args = ('standings', 'next_game') return args - def url_builder(self, args): + def url_builder(self, args, logo): self.headers.update({'x-apisports-host': 'v1.baseball.api-sports.io'}) urls = {} base = "https://v1.baseball.api-sports.io/" urls.update({'standings': base + f'standings?league=1&season=2021'}) - urls.update({'next_game': base + f"games?team={self.config.getint('team_id')}&league=1&season=2021&timezone=America/Chicago"}) + urls.update({'next_game': base + f"games?team={logo.apisportsid}&league=1&season=2021&timezone=America/Chicago"}) return urls - async def run(self): - self.logger.info('Running Baseball API') + async def run(self) -> SportApiResult: + self.logger.info('Running baseball API') parsed = self.parse_args() api_data = {} - for section, url in self.url_builder(parsed).items(): + logo = logo_map[self.config.get('team_id')] + for section, url in self.url_builder(parsed, logo).items(): api_data.update({section: await self.get_data(url, self.headers)}) api_data['sport'] = 'baseball' - return SportApiResult(api_data) + schedule = [ + base.Game( + team=base.Team( + name=logo.name, + logo=logo, + ), + timestamp=datetime.fromtimestamp(game['timestamp']), + status=base.determine_apisports_game_status( + game, + base.Team( + logo.name, + logo=logo + ), + api_data['sport'] + )[0], + opposing_team=base.Team( + name=game['teams']['home']['name'], + logo=logo_map[game['teams']['home']['name']], + ) if logo.name != game['teams']['home']['name'] else base.Team( + name=game['teams']['away']['name'], logo=logo_map[game['teams']['away']['name']] + ), + result=base.determine_apisports_game_status( + game, + base.Team( + logo.name, + logo=logo + ), + api_data['sport'] + )[1], + homeoraway="home" if logo.name == game['teams']['home']['name'] else "away", + score=base.Score( + team=game['scores']['home'] if logo.name == game['teams']['home']['name'] else game['scores']['away'], + opposing_team=game['scores']['home'] if logo.name != game['teams']['home']['name'] else game['scores']['away'], + ), + + ) for game in api_data['next_game']['response'] + ] + standings=[ + base.Team( + name=team['team']['name'], + logo=logo_map[team['team']['name']], + position=team['position'], + league=team['group']['name'] + ) + for team in api_data['standings']['response'][0] + api_data['standings']['response'][1] + ] + result = base.ModuleResult( + name=logo.name, + team=base.Team( + name=logo.name, + logo=logo, + ), + schedule=schedule, + standings = standings, + sport=base.SportStructure.Basketball, + games_played=len([game for game in schedule if game.status == base.GameStatus.Finished or game.status == base.GameStatus.Overtime]), + wins=len([game for game in schedule if game.result == base.GameResult.WIN]), + losses=len([game for game in schedule if game.result == base.GameResult.LOSS]), + ) + return SportApiResult(result) diff --git a/lib/sports/apisports/basketball/basketball.py b/lib/sports/apisports/basketball/basketball.py index 8d45298..c5e4f30 100755 --- a/lib/sports/apisports/basketball/basketball.py +++ b/lib/sports/apisports/basketball/basketball.py @@ -1,14 +1,16 @@ from lib.run import Runner from datetime import datetime from lib.sports.apisports.result import SportApiResult - +from lib.sports.logo import logo_map, Logo +from datetime import datetime +import lib.sports.sportbase as base class Basketball(Runner): def __init__(self, token, config, headers): self.config = config self.token = token self.headers = headers self.year = datetime.now().year - + def parse_args(self): try: if self.config['type'] != '' or not self.config['type']: @@ -19,22 +21,81 @@ def parse_args(self): except KeyError: args = ('standings', 'next_game') return args - - def url_builder(self, args): + + def url_builder(self, args, logo): self.headers.update({'x-apisports-host': 'v1.basketball.api-sports.io'}) urls = {} base = "https://v1.basketball.api-sports.io/" if 'standings' in args: - urls.update({'standings': base + f'standings?league=12&season=2019-2020'}) + urls.update({'standings': base + f'standings?league=12&season=2021-2022'}) if 'next_game' in args: - urls.update({'next_game': base + f"games?team={self.config.getint('team_id')}&league=12&season=2019-2020&timezone=America/Chicago"}) + urls.update({'next_game': base + f"games?team={logo.apisportsid}&league=12&season=2021-2022&timezone=America/Chicago"}) return urls - async def run(self): - self.logger.info('Running Basketball API') + async def run(self) -> SportApiResult: + self.logger.info('Running baseball API') parsed = self.parse_args() api_data = {} - for section, url in self.url_builder(parsed).items(): + logo = logo_map[self.config.get('team_id')] + for section, url in self.url_builder(parsed, logo).items(): api_data.update({section: await self.get_data(url, self.headers)}) api_data['sport'] = 'basketball' - return SportApiResult(api_data) \ No newline at end of file + schedule = [ + base.Game( + team=base.Team( + name=logo.name, + logo=logo, + ), + timestamp=datetime.fromtimestamp(game['timestamp']), + status=base.determine_apisports_game_status( + game, + base.Team( + logo.name, + logo=logo + ), + api_data['sport'] + )[0], + opposing_team=base.Team( + name=game['teams']['home']['name'], + logo=logo_map[game['teams']['home']['name']], + ) if logo.name != game['teams']['home']['name'] else base.Team( + name=game['teams']['away']['name'], logo=logo_map[game['teams']['away']['name']] + ), + result=base.determine_apisports_game_status( + game, + base.Team( + logo.name, + logo=logo + ), + api_data['sport'] + )[1], + homeoraway="home" if logo.name == game['teams']['home']['name'] else "away", + score=base.Score( + team=game['scores']['home'] if logo.name == game['teams']['home']['name'] else game['scores']['away'], + opposing_team=game['scores']['home'] if logo.name != game['teams']['home']['name'] else game['scores']['away'], + ), + + ) for game in api_data['next_game']['response'] + ] + standings=[ + base.Team( + name=team['team']['name'], + logo=logo_map[team['team']['name']], + position=team['position'], + league=team['group']['name'] + ) for team in api_data['standings']['response'][0] + ] + result = base.ModuleResult( + name=logo.name, + team=base.Team( + name=logo.name, + logo=logo, + ), + schedule=schedule, + standings = standings, + sport=base.SportStructure.Basketball, + games_played=len([game for game in schedule if game.status == base.GameStatus.Finished or game.status == base.GameStatus.Overtime]), + wins=len([game for game in schedule if game.result == base.GameResult.WIN]), + losses=len([game for game in schedule if game.result == base.GameResult.LOSS]), + ) + return SportApiResult(result) \ No newline at end of file diff --git a/lib/sports/apisports/hockey/hockey.py b/lib/sports/apisports/hockey/hockey.py index a5b9cad..62dd0a6 100755 --- a/lib/sports/apisports/hockey/hockey.py +++ b/lib/sports/apisports/hockey/hockey.py @@ -1,6 +1,10 @@ from lib.run import Runner from datetime import datetime from lib.sports.apisports.result import SportApiResult +from lib.sports.logo import logo_map, Logo +from datetime import datetime +import lib.sports.sportbase as base + class Hockey(Runner): def __init__(self, token, config, headers): @@ -20,21 +24,81 @@ def parse_args(self): args = ('standings', 'next_game') return args - def url_builder(self, args): + def url_builder(self, args, logo): self.headers.update({'x-apisports-host': 'v1.hockey.api-sports.io'}) urls = {} base = "https://v1.hockey.api-sports.io/" if 'standings' in args: - urls.update({'standings': base + f'standings?league=57&season=2020'}) + urls.update({'standings': base + f'standings?league=57&season=2021'}) if 'next_game' in args: - urls.update({'next_game': base + f"games?team={self.config.getint('team_id')}&league=57&season=2020&timezone=America/Chicago"}) + urls.update({'next_game': base + f"games?team={logo.apisportsid}&league=57&season=2021&timezone=America/Chicago"}) return urls async def run(self) -> SportApiResult: self.logger.info('Running Hockey API') parsed = self.parse_args() api_data = {} - for section, url in self.url_builder(parsed).items(): + logo = logo_map[self.config.get('team_id')] + for section, url in self.url_builder(parsed, logo).items(): api_data.update({section: await self.get_data(url, self.headers)}) api_data['sport'] = 'hockey' - return SportApiResult(api_data) \ No newline at end of file + schedule = [ + base.Game( + team=base.Team( + name=logo.name, + logo=logo, + ), + timestamp=datetime.fromtimestamp(game['timestamp']), + status=base.determine_apisports_game_status( + game, + base.Team( + logo.name, + logo=logo + ), + api_data['sport'] + )[0], + opposing_team=base.Team( + name=game['teams']['home']['name'], + logo=logo_map[game['teams']['home']['name']], + ) if logo.name != game['teams']['home']['name'] else base.Team( + name=game['teams']['away']['name'], logo=logo_map[game['teams']['away']['name']] + ), + result=base.determine_apisports_game_status( + game, + base.Team( + logo.name, + logo=logo + ), + api_data['sport'] + )[1], + homeoraway="home" if logo.name == game['teams']['home']['name'] else "away", + score=base.Score( + team=game['scores']['home'] if logo.name == game['teams']['home']['name'] else game['scores']['away'], + opposing_team=game['scores']['home'] if logo.name != game['teams']['home']['name'] else game['scores']['away'], + ), + + ) for game in api_data['next_game']['response'] + ] + standings=[ + base.Team( + name=team['team']['name'], + logo=logo_map[team['team']['name']], + position=team['position'], + league=team['group']['name'] + ) + for team in api_data['standings']['response'][0] + api_data['standings']['response'][1] + ] + result = base.ModuleResult( + name=logo.name, + team=base.Team( + name=logo.name, + logo=logo, + ), + schedule=schedule, + standings = standings, + sport=base.SportStructure.Hockey, + games_played=len([game for game in schedule if game.status == base.GameStatus.Finished or game.status == base.GameStatus.Overtime]), + wins=len([game for game in schedule if game.result == base.GameResult.WIN]), + losses=len([game for game in schedule if game.result == base.GameResult.LOSS]), + ) + return SportApiResult(result) \ No newline at end of file diff --git a/lib/sports/apisports/result.py b/lib/sports/apisports/result.py index 97c771c..cb07d09 100644 --- a/lib/sports/apisports/result.py +++ b/lib/sports/apisports/result.py @@ -1,116 +1,75 @@ -from lib.sports.sportbase import SportResultBase - import json +from asyncio import Task +from typing import Dict, List, Tuple +from datetime import datetime +from enum import Enum +from sportsipy.nhl.schedule import Game +from sportsipy.nhl.teams import Team as nhl_team +from lib.sports.sportbase import SportResultBase, API +from lib.sports.logo import Logo, logo_map +import lib.sports.sportbase as base class SportApiResult(SportResultBase): - def __init__(self, api) -> None: + def __init__(self, api_result) -> None: super().__init__() - self.api_type = "" - self.api = api - self.main_sport = api - self._sport = api['sport'] - if len(self.main_sport['standings']['errors']) != 0: - self._error = self.set_error() + self.api_result = api_result + self._get_sport: Enum = api_result.sport + self._team: base.Team = api_result.team + self._schedule: base.SportStandings = base.SportStandings( + positions=api_result.schedule + ) + + self._api: Enum = API.APISPORTS + self._standings: List[base.Team] = api_result.standings + self._position = self._team.position + self._get_leagues = None + self._games_played: List[Game] = self._schedule.positions[:api_result.games_played] if api_result.games_played <= len(self._schedule.positions) else [] + self._get_wins: List[Game] = [game for game in self._games_played if base.GameResult.WIN == game.result] + self._win_percentage: float = api_result.wins/len(self._games_played) + self._losses: List[Game] = [game for game in self._games_played if base.GameResult.LOSS == game.result] + self._loss_percentage: float = api_result.losses/len(self._games_played) + if len(self._games_played) == len(self._schedule.positions): + self._next_game = [] else: - self._error = self.set_error() - self.standings = self.build_standings() - self._length = len(self.standings) - self._positions = [(team.get('name'), team.get('position')) for team in self.standings] - self._leagues = [(team.get('name'), team.get('league')) for team in self.standings] - self._games_played = [(team.get('name'), team.get('games').get('played')) for team in self.standings] - self._wins = [(team.get('name'), team['games']['win']['total']) for team in self.standings] - self._wins_percentage = [(team.get('name'), team['games']['win']['percentage']) for team in self.standings] - self._losses = [(team.get('name'), team['games']['lose']['total']) for team in self.standings] - self._loss_percentage = [(team.get('name'), team['games']['lose']['percentage']) for team in self.standings] - self.next_game = self.build_nextgame() - self._game_ids = [game.get('game_id') for game in self.next_game] - self._timestamps = [(game.get('game_id'), game.get('timestamp')) for game in self.next_game] - self._teams = [(game.get('game_id'), game.get('teams')) for game in self.next_game] - self._vs = [(game.get('game_id'), (game['teams']['home']['name'], game['teams']['away']['name'])) for game in self.next_game] - self._status = [(game.get('game_id'), game.get('status')) for game in self.next_game] - self._game_result = {game.get('game_id'): game.get('score') for game in self.next_game} - - def __repr__(self): - attrs = [ - f"length={self._length}", - f"positions={json.dumps(self._positions, indent=2)}", - f'leagues={json.dumps(self._leagues, indent=2)}', - f"games_played={json.dumps(self._games_played, indent=2)}", - f"wins={json.dumps(self._wins, indent=2)}", - f"wins_percentage={json.dumps(self._wins_percentage, indent=2)}", - f"losses={json.dumps(self._losses, indent=2)}", - f"loss_percentage={json.dumps(self._loss_percentage, indent=2)}", - f"game_ids={json.dumps(self._game_ids, indent=2)}", - f"timestamps={json.dumps(self._timestamps, indent=2)}", - f"teams={json.dumps(self._teams, indent=2)}", - f"vs={json.dumps(self._vs, indent=2)}", - f"status={json.dumps(self._status, indent=2)}", - f"game_result={json.dumps(self._game_result, indent=2)}" - ] - joined = "\t\n".join(attrs) - return f"Sport(\n{joined})" + self._next_game = self._schedule.positions[len(self._games_played)] if len(self._games_played) < len(self._schedule.positions) else [] + self._game_ids = None - def build_standings(self): - #counter = 0 - position = [] - regular_season_check = ( - "MLB - Regular Season", - "NBA - Regular Season", - "NHL - Regular Season", - "NFL - Regular Season" - ) - # Can Be Empty Must try and except for that - for pos in self.main_sport['standings'].get('response')[0]: - if not pos.get('stage') in regular_season_check: - continue - position.append({'name': pos.get('team').get('name'), - 'position': pos.get('position'), - 'league': pos.get('group').get('name'), - 'games': pos.get('games') - }) - return position - def build_nextgame(self): - main = [] - for game in self.main_sport['next_game'].get('response'): - main.append({ - 'game_id': game.get('id'), - 'timestamp': game.get('timestamp'), - 'status': game['status']['short'], - 'teams': game['teams'], - 'score': game['scores'] - }) - return main + @property + def get_api(self) -> Enum: + return self._api - def set_error(self): - if isinstance(self.main_sport['standings']['errors'], list): - return True, "" - else: - return False, self.main_sport['standings']['errors']['requests'] + @property + def get_sport(self) -> Enum: + return self._get_sport + + @property + def team_name(self): + return self._team.name @property - def get_sport(self): - return self._sport + def get_logo(self) -> Logo: + return logo_map[self._team.name] @property - def get_error(self): - return self._error - + def get_team(self): + return self._team + @property def get_length_position_teams(self): - return len(self.standings) + return len(self._standings) @property def get_standings(self): - return self.standings + return self._standings - @property - def get_position_teams(self): - return self._positions + @property + def get_schedule(self): + return self._schedule @property def get_leagues(self): - return self._leagues + return self._get_leagues @property def get_games_played(self): @@ -118,11 +77,11 @@ def get_games_played(self): @property def get_wins(self): - return self._wins + return self._get_wins @property def get_wins_percentage(self): - return self._wins_percentage + return self._win_percentage @property def get_losses(self): @@ -136,26 +95,9 @@ def get_loss_percentage(self): def get_game_ids(self): return self._game_ids - @property - def get_timestamps(self): - return self._timestamps - - @property - def get_teams(self): - return self._teams - - @property - def get_versus(self): - return self._vs - - @property - def get_status(self): - return self._status - - @property - def get_scores(self): - return self._game_result - def get_specific_score(self, game_id): return self._game_result.get(game_id) + @property + def get_next_game(self): + return self._next_game \ No newline at end of file diff --git a/lib/sports/logo.py b/lib/sports/logo.py new file mode 100644 index 0000000..c6f89c9 --- /dev/null +++ b/lib/sports/logo.py @@ -0,0 +1,171 @@ +from dataclasses import dataclass +from typing import Tuple + +@dataclass(repr=True) +class Logo: + name: str + sportsdb_leagueid: int + url: str + sport: str + shorthand: str + apisportsid: int + sportsdbid: int + sportsipyid: int + +baseball_teams = { + 'SFG': 'San Francisco Giants', + 'LAD': 'Los Angeles Dodgers', + 'TBR': 'Tampa Bay Rays', + 'HOU': 'Houston Astros', + 'MIL': 'Milwaukee Brewers', + 'CHW': 'Chicago White Sox', + 'BOS': 'Boston Red Sox', + 'NYY': 'New York Yankees', + 'TOR': 'Toronto Blue Jays', + 'STL': 'St. Louis Cardinals', + 'SEA': 'Seattle Mariners', + 'ATL': 'Atlanta Braves', + 'OAK': 'Oakland Athletics', + 'CIN': 'Cincinnati Reds', + 'PHI': 'Philadelphia Phillies', + 'CLE': 'Cleveland Indians', + 'SDP': 'San Diego Padres', + 'DET': 'Detroit Tigers', + 'NYM': 'New York Mets', + 'LAA': 'Los Angeles Angels', + 'COL': 'Colorado Rockies', + 'KCR': 'Kansas City Royals', + 'MIN': 'Minnesota Twins', + 'CHC': 'Chicago Cubs', + 'MIA': 'Miami Marlins', + 'WSN': 'Washington Nationals', + 'PIT': 'Pittsburgh Pirates', + 'TEX': 'Texas Rangers', + 'ARI': 'Arizona Diamondbacks', + 'BAL': 'Baltimore Orioles' +} +logo_map = {'Arizona Diamondbacks': Logo(name='Arizona Diamondbacks', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/sutyqp1431251804.png', sport='Baseball', shorthand='ARI', apisportsid=2, sportsdbid=135267, sportsipyid=None), + 'Atlanta Braves': Logo(name='Atlanta Braves', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/yjs76e1617811496.png', sport='Baseball', shorthand='ATL', apisportsid=3, sportsdbid=135268, sportsipyid=None), + 'Baltimore Orioles': Logo(name='Baltimore Orioles', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/ytywvu1431257088.png', sport='Baseball', shorthand='BAL', apisportsid=4, sportsdbid=135251, sportsipyid=None), + 'Boston Red Sox': Logo(name='Boston Red Sox', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/stpsus1425120215.png', sport='Baseball', shorthand='BOS', apisportsid=5, sportsdbid=135252, sportsipyid=None), + 'Chicago Cubs': Logo(name='Chicago Cubs', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/wxbe071521892391.png', sport='Baseball', shorthand='CHC', apisportsid=6, sportsdbid=135269, sportsipyid=None), + 'Chicago White Sox': Logo(name='Chicago White Sox', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/yyz5dh1554140884.png', sport='Baseball', shorthand='CWS', apisportsid=7, sportsdbid=135253, sportsipyid=None), + 'Cincinnati Reds': Logo(name='Cincinnati Reds', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/wspusr1431538832.png', sport='Baseball', shorthand='CIN', apisportsid=8, sportsdbid=135270, sportsipyid=None), + 'Cleveland Indians': Logo(name='Cleveland Indians', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/fp39hu1521904440.png', sport='Baseball', shorthand='CLE', apisportsid=9, sportsdbid=135254, sportsipyid=None), + 'Colorado Rockies': Logo(name='Colorado Rockies', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/wvbk1d1550584627.png', sport='Baseball', shorthand='COL', apisportsid=10, sportsdbid=135271, sportsipyid=None), + 'Detroit Tigers': Logo(name='Detroit Tigers', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/9dib6o1554032173.png', sport='Baseball', shorthand='DET', apisportsid=12, sportsdbid=135255, sportsipyid=None), + 'Houston Astros': Logo(name='Houston Astros', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/miwigx1521893583.png', sport='Baseball', shorthand='HOU', apisportsid=15, sportsdbid=135256, sportsipyid=None), + 'Kansas City Royals': Logo(name='Kansas City Royals', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/ii3rz81554031260.png', sport='Baseball', shorthand='KC', apisportsid=16, sportsdbid=135257, sportsipyid=None), + 'Los Angeles Angels': Logo(name='Los Angeles Angels', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/vswsvx1432577476.png', sport='Baseball', shorthand='LAA', apisportsid=17, sportsdbid=135258, sportsipyid=None), + 'Los Angeles Dodgers': Logo(name='Los Angeles Dodgers', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/rrdfmw1617528853.png', sport='Baseball', shorthand='LAD', apisportsid=18, sportsdbid=135272, sportsipyid=None), + 'Miami Marlins': Logo(name='Miami Marlins', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/0722fs1546001701.png', sport='Baseball', shorthand='MIA', apisportsid=19, sportsdbid=135273, sportsipyid=None), + 'Milwaukee Brewers': Logo(name='Milwaukee Brewers', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/08kh2a1595775193.png', sport='Baseball', shorthand='MIL', apisportsid=20, sportsdbid=135274, sportsipyid=None), + 'Minnesota Twins': Logo(name='Minnesota Twins', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/necd5v1521905719.png', sport='Baseball', shorthand='MIN', apisportsid=22, sportsdbid=135259, sportsipyid=None), + 'New York Mets': Logo(name='New York Mets', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/rxqspq1431540337.png', sport='Baseball', shorthand='NYM', apisportsid=24, sportsdbid=135275, sportsipyid=None), + 'New York Yankees': Logo(name='New York Yankees', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/wqwwxx1423478766.png', sport='Baseball', shorthand='NYY', apisportsid=25, sportsdbid=135260, sportsipyid=None), + 'Oakland Athletics': Logo(name='Oakland Athletics', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/wsxtyw1432577334.png', sport='Baseball', shorthand='OAK', apisportsid=26, sportsdbid=135261, sportsipyid=None), + 'Philadelphia Phillies': Logo(name='Philadelphia Phillies', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/3xrldf1617528682.png', sport='Baseball', shorthand='PHI', apisportsid=27, sportsdbid=135276, sportsipyid=None), + 'Pittsburgh Pirates': Logo(name='Pittsburgh Pirates', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/kw6uqr1617527138.png', sport='Baseball', shorthand='PIT', apisportsid=28, sportsdbid=135277, sportsipyid=None), + 'San Diego Padres': Logo(name='San Diego Padres', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/6wt1cn1617527530.png', sport='Baseball', shorthand='SD', apisportsid=30, sportsdbid=135278, sportsipyid=None), + 'San Francisco Giants': Logo(name='San Francisco Giants', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/mq81yb1521896622.png', sport='Baseball', shorthand='SF', apisportsid=31, sportsdbid=135279, sportsipyid=None), + 'Seattle Mariners': Logo(name='Seattle Mariners', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/39x9ph1521903933.png', sport='Baseball', shorthand='SEA', apisportsid=32, sportsdbid=135262, sportsipyid=None), + 'St. Louis Cardinals': Logo(name='St. Louis Cardinals', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/uvyvyr1424003273.png', sport='Baseball', shorthand='STL', apisportsid=33, sportsdbid=135280, sportsipyid=None), + 'St.Louis Cardinals': Logo(name='St. Louis Cardinals', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/uvyvyr1424003273.png', sport='Baseball', shorthand='STL', apisportsid=33, sportsdbid=135280, sportsipyid=None), + 'Tampa Bay Rays': Logo(name='Tampa Bay Rays', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/littyt1554031623.png', sport='Baseball', shorthand='TB', apisportsid=34, sportsdbid=135263, sportsipyid=None), + 'Texas Rangers': Logo(name='Texas Rangers', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/qt9qki1521893151.png', sport='Baseball', shorthand='TEX', apisportsid=35, sportsdbid=135264, sportsipyid=None), + 'Toronto Blue Jays': Logo(name='Toronto Blue Jays', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/f9zk3l1617527686.png', sport='Baseball', shorthand='TOR', apisportsid=36, sportsdbid=135265, sportsipyid=None), + 'Washington Nationals': Logo(name='Washington Nationals', sportsdb_leagueid=4424, url='https://www.thesportsdb.com/images/media/team/badge/wpqrut1423694764.png', sport='Baseball', shorthand='WAS', apisportsid=37, sportsdbid=135281, sportsipyid=None), + 'Atlanta Hawks': Logo(name='Atlanta Hawks', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/q3bx641635067495.png', sport='Basketball', shorthand='ATL', apisportsid=132, sportsdbid=134880, sportsipyid=None), + 'Boston Celtics': Logo(name='Boston Celtics', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/051sjd1537102179.png', sport='Basketball', shorthand='BOS', apisportsid=133, sportsdbid=134860, sportsipyid=None), + 'Brooklyn Nets': Logo(name='Brooklyn Nets', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/h0dwny1600552068.png', sport='Basketball', shorthand='BKN', apisportsid=134, sportsdbid=134861, sportsipyid=None), + 'Charlotte Hornets': Logo(name='Charlotte Hornets', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/xqtvvp1422380623.png', sport='Basketball', shorthand='CHA', apisportsid=135, sportsdbid=134881, sportsipyid=None), + 'Chicago Bulls': Logo(name='Chicago Bulls', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/yk7swg1547214677.png', sport='Basketball', shorthand='CHI', apisportsid=136, sportsdbid=134870, sportsipyid=None), + 'Cleveland Cavaliers': Logo(name='Cleveland Cavaliers', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/a2pp4c1503741152.png', sport='Basketball', shorthand='CLE', apisportsid=137, sportsdbid=134871, sportsipyid=None), + 'Dallas Mavericks': Logo(name='Dallas Mavericks', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/yqrxrs1420568796.png', sport='Basketball', shorthand='DAL', apisportsid=138, sportsdbid=134875, sportsipyid=None), + 'Denver Nuggets': Logo(name='Denver Nuggets', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/8o8j5k1546016274.png', sport='Basketball', shorthand='DEN', apisportsid=139, sportsdbid=134885, sportsipyid=None), + 'Detroit Pistons': Logo(name='Detroit Pistons', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/lg7qrc1621594751.png', sport='Basketball', shorthand='DET', apisportsid=140, sportsdbid=134872, sportsipyid=None), + 'Golden State Warriors': Logo(name='Golden State Warriors', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/irobi61565197527.png', sport='Basketball', shorthand='GSW', apisportsid=141, sportsdbid=134865, sportsipyid=None), + 'Houston Rockets': Logo(name='Houston Rockets', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/yezpho1597486052.png', sport='Basketball', shorthand='HOU', apisportsid=142, sportsdbid=134876, sportsipyid=None), + 'Indiana Pacers': Logo(name='Indiana Pacers', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/v6jzgm1503741821.png', sport='Basketball', shorthand='IND', apisportsid=143, sportsdbid=134873, sportsipyid=None), + 'Los Angeles Clippers': Logo(name='Los Angeles Clippers', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/jv7tf21545916958.png', sport='Basketball', shorthand='LAC', apisportsid=144, sportsdbid=134866, sportsipyid=None), + 'Los Angeles Lakers': Logo(name='Los Angeles Lakers', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/spa6c11621594682.png', sport='Basketball', shorthand='LAL', apisportsid=145, sportsdbid=134867, sportsipyid=None), + 'Memphis Grizzlies': Logo(name='Memphis Grizzlies', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/m64v461565196789.png', sport='Basketball', shorthand='MEM', apisportsid=146, sportsdbid=134877, sportsipyid=None), + 'Miami Heat': Logo(name='Miami Heat', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/5v67x51547214763.png', sport='Basketball', shorthand='MIA', apisportsid=147, sportsdbid=134882, sportsipyid=None), + 'Milwaukee Bucks': Logo(name='Milwaukee Bucks', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/olhug01621594702.png', sport='Basketball', shorthand='MIL', apisportsid=148, sportsdbid=134874, sportsipyid=None), + 'Minnesota Timberwolves': Logo(name='Minnesota Timberwolves', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/5xpgjg1621594771.png', sport='Basketball', shorthand='MIN', apisportsid=149, sportsdbid=134886, sportsipyid=None), + 'New Orleans Pelicans': Logo(name='New Orleans Pelicans', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/f341s31523700397.png', sport='Basketball', shorthand='NOP', apisportsid=150, sportsdbid=134878, sportsipyid=None), + 'New York Knicks': Logo(name='New York Knicks', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/wyhpuf1511810435.png', sport='Basketball', shorthand='NYK', apisportsid=151, sportsdbid=134862, sportsipyid=None), + 'Oklahoma City Thunder': Logo(name='Oklahoma City Thunder', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/xpswpq1422575434.png', sport='Basketball', shorthand='OKC', apisportsid=152, sportsdbid=134887, sportsipyid=None), + 'Orlando Magic': Logo(name='Orlando Magic', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/txuyrr1422492990.png', sport='Basketball', shorthand='ORL', apisportsid=153, sportsdbid=134883, sportsipyid=None), + 'Philadelphia 76ers': Logo(name='Philadelphia 76ers', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/71545f1518464849.png', sport='Basketball', shorthand='PHI', apisportsid=154, sportsdbid=134863, sportsipyid=None), + 'Phoenix Suns': Logo(name='Phoenix Suns', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/qrtuxq1422919040.png', sport='Basketball', shorthand='PHX', apisportsid=155, sportsdbid=134868, sportsipyid=None), + 'Portland Trail Blazers': Logo(name='Portland Trail Blazers', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/mbtzin1520794112.png', sport='Basketball', shorthand='POR', apisportsid=156, sportsdbid=134888, sportsipyid=None), + 'Sacramento Kings': Logo(name='Sacramento Kings', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/5d3dpz1611859587.png', sport='Basketball', shorthand='SAC', apisportsid=157, sportsdbid=134869, sportsipyid=None), + 'San Antonio Spurs': Logo(name='San Antonio Spurs', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/obucan1611859537.png', sport='Basketball', shorthand='SAS', apisportsid=158, sportsdbid=134879, sportsipyid=None), + 'Toronto Raptors': Logo(name='Toronto Raptors', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/ax36vz1635070057.png', sport='Basketball', shorthand='TOR', apisportsid=159, sportsdbid=134864, sportsipyid=None), + 'Utah Jazz': Logo(name='Utah Jazz', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/9p1e5j1572041084.png', sport='Basketball', shorthand='UTA', apisportsid=160, sportsdbid=134889, sportsipyid=None), + 'Washington Wizards': Logo(name='Washington Wizards', sportsdb_leagueid=4387, url='https://www.thesportsdb.com/images/media/team/badge/rhxi9w1621594729.png', sport='Basketball', shorthand='WAS', apisportsid=161, sportsdbid=134884, sportsipyid=None), + 'Arizona Cardinals': Logo(name='Arizona Cardinals', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/xvuwtw1420646838.png', sport='American Football', shorthand='ARI', apisportsid=0, sportsdbid=134946, sportsipyid=None), + 'Atlanta Falcons': Logo(name='Atlanta Falcons', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/rrpvpr1420658174.png', sport='American Football', shorthand='ATL', apisportsid=0, sportsdbid=134942, sportsipyid=None), + 'Baltimore Ravens': Logo(name='Baltimore Ravens', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/einz3p1546172463.png', sport='American Football', shorthand='BAL', apisportsid=0, sportsdbid=134922, sportsipyid=None), + 'Buffalo Bills': Logo(name='Buffalo Bills', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/6pb37b1515849026.png', sport='American Football', shorthand='BUF', apisportsid=0, sportsdbid=134918, sportsipyid=None), + 'Carolina Panthers': Logo(name='Carolina Panthers', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/xxyvvy1420940478.png', sport='American Football', shorthand='CAR', apisportsid=0, sportsdbid=134943, sportsipyid=None), + 'Chicago Bears': Logo(name='Chicago Bears', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/uwtwtv1420941123.png', sport='American Football', shorthand='CHI', apisportsid=0, sportsdbid=134938, sportsipyid=None), + 'Cincinnati Bengals': Logo(name='Cincinnati Bengals', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/qqtwwv1420941670.png', sport='American Football', shorthand='CIN', apisportsid=0, sportsdbid=134923, sportsipyid=None), + 'Cleveland Browns': Logo(name='Cleveland Browns', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/squvxy1420942389.png', sport='American Football', shorthand='CLE', apisportsid=0, sportsdbid=134924, sportsipyid=None), + 'Dallas Cowboys': Logo(name='Dallas Cowboys', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/wrxssu1450018209.png', sport='American Football', shorthand='DAL', apisportsid=0, sportsdbid=134934, sportsipyid=None), + 'Denver Broncos': Logo(name='Denver Broncos', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/upsspx1421635647.png', sport='American Football', shorthand='DEN', apisportsid=0, sportsdbid=134930, sportsipyid=None), + 'Detroit Lions': Logo(name='Detroit Lions', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/lgsgkr1546168257.png', sport='American Football', shorthand='DET', apisportsid=0, sportsdbid=134939, sportsipyid=None), + 'Green Bay Packers': Logo(name='Green Bay Packers', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/rqpwtr1421434717.png', sport='American Football', shorthand='GB', apisportsid=0, sportsdbid=134940, sportsipyid=None), + 'Houston Texans': Logo(name='Houston Texans', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/wqyryy1421436627.png', sport='American Football', shorthand='HOU', apisportsid=0, sportsdbid=134926, sportsipyid=None), + 'Indianapolis Colts': Logo(name='Indianapolis Colts', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/wqqvpx1421434058.png', sport='American Football', shorthand='IND', apisportsid=0, sportsdbid=134927, sportsipyid=None), + 'Jacksonville Jaguars': Logo(name='Jacksonville Jaguars', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/0mrsd41546427902.png', sport='American Football', shorthand='JAX', apisportsid=0, sportsdbid=134928, sportsipyid=None), + 'Kansas City Chiefs': Logo(name='Kansas City Chiefs', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/936t161515847222.png', sport='American Football', shorthand='KC', apisportsid=0, sportsdbid=134931, sportsipyid=None), + 'Las Vegas Raiders': Logo(name='Las Vegas Raiders', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/xqusqy1421724291.png', sport='American Football', shorthand='OAK', apisportsid=0, sportsdbid=134932, sportsipyid=None), + 'Los Angeles Chargers': Logo(name='Los Angeles Chargers', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/wbhu3a1548320628.png', sport='American Football', shorthand='LAC', apisportsid=None, sportsdbid=135908, sportsipyid=None), + 'Los Angeles Rams': Logo(name='Los Angeles Rams', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/8e8v4i1599764614.png', sport='American Football', shorthand='LA', apisportsid=None, sportsdbid=135907, sportsipyid=None), + 'Miami Dolphins': Logo(name='Miami Dolphins', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/trtusv1421435081.png', sport='American Football', shorthand='MIA', apisportsid=0, sportsdbid=134919, sportsipyid=None), + 'Minnesota Vikings': Logo(name='Minnesota Vikings', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/qstqqr1421609163.png', sport='American Football', shorthand='MIN', apisportsid=0, sportsdbid=134941, sportsipyid=None), + 'New England Patriots': Logo(name='New England Patriots', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/xtwxyt1421431860.png', sport='American Football', shorthand='NE', apisportsid=0, sportsdbid=134920, sportsipyid=None), + 'New Orleans Saints': Logo(name='New Orleans Saints', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/nd46c71537821337.png', sport='American Football', shorthand='NO', apisportsid=0, sportsdbid=134944, sportsipyid=None), + 'New York Giants': Logo(name='New York Giants', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/vxppup1423669459.png', sport='American Football', shorthand='NYG', apisportsid=0, sportsdbid=134935, sportsipyid=None), + 'New York Jets': Logo(name='New York Jets', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/hz92od1607953467.png', sport='American Football', shorthand='NYJ', apisportsid=0, sportsdbid=134921, sportsipyid=None), + 'Philadelphia Eagles': Logo(name='Philadelphia Eagles', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/pnpybf1515852421.png', sport='American Football', shorthand='PHI', apisportsid=0, sportsdbid=134936, sportsipyid=None), + 'Pittsburgh Steelers': Logo(name='Pittsburgh Steelers', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/2975411515853129.png', sport='American Football', shorthand='PIT', apisportsid=0, sportsdbid=134925, sportsipyid=None), + 'San Francisco 49ers': Logo(name='San Francisco 49ers', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/bqbtg61539537328.png', sport='American Football', shorthand='SF', apisportsid=0, sportsdbid=134948, sportsipyid=None), + 'Seattle Seahawks': Logo(name='Seattle Seahawks', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/wwuqyr1421434817.png', sport='American Football', shorthand='SEA', apisportsid=0, sportsdbid=134949, sportsipyid=None), + 'Tampa Bay Buccaneers': Logo(name='Tampa Bay Buccaneers', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/2dfpdl1537820969.png', sport='American Football', shorthand='TB', apisportsid=0, sportsdbid=134945, sportsipyid=None), + 'Tennessee Titans': Logo(name='Tennessee Titans', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/m48yia1515847376.png', sport='American Football', shorthand='TEN', apisportsid=0, sportsdbid=134929, sportsipyid=None), + 'Washington Football Team': Logo(name='Washington', sportsdb_leagueid=4391, url='https://www.thesportsdb.com/images/media/team/badge/1m3mzp1595609069.png', sport='American Football', shorthand='WAS', apisportsid=0, sportsdbid=134937, sportsipyid=None), + 'Anaheim Ducks': Logo(name='Anaheim Ducks', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/6g9t721547289240.png', sport='Ice Hockey', shorthand='ANA', apisportsid=670, sportsdbid=134846, sportsipyid=None), + 'Arizona Coyotes': Logo(name='Arizona Coyotes', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/3n1yqw1635072720.png', sport='Ice Hockey', shorthand='ARI', apisportsid=1460, sportsdbid=134847, sportsipyid=None), + 'Boston Bruins': Logo(name='Boston Bruins', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/vuspuq1421791546.png', sport='Ice Hockey', shorthand='BOS', apisportsid=673, sportsdbid=134830, sportsipyid=None), + 'Buffalo Sabres': Logo(name='Buffalo Sabres', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/3m3jhp1619536655.png', sport='Ice Hockey', shorthand='BUF', apisportsid=674, sportsdbid=134831, sportsipyid=None), + 'Calgary Flames': Logo(name='Calgary Flames', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/v8vkk11619536610.png', sport='Ice Hockey', shorthand='CGY', apisportsid=675, sportsdbid=134848, sportsipyid=None), + 'Carolina Hurricanes': Logo(name='Carolina Hurricanes', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/v07m3x1547232585.png', sport='Ice Hockey', shorthand='CAR', apisportsid=676, sportsdbid=134838, sportsipyid=None), + 'Chicago Blackhawks': Logo(name='Chicago Blackhawks', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/tuwyvr1422041801.png', sport='Ice Hockey', shorthand='CHI', apisportsid=678, sportsdbid=134854, sportsipyid=None), + 'Colorado Avalanche': Logo(name='Colorado Avalanche', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/wqutut1421173572.png', sport='Ice Hockey', shorthand='COL', apisportsid=679, sportsdbid=134855, sportsipyid=None), + 'Columbus Blue Jackets': Logo(name='Columbus Blue Jackets', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/ssytwt1421792535.png', sport='Ice Hockey', shorthand='CBJ', apisportsid=680, sportsdbid=134839, sportsipyid=None), + 'Dallas Stars': Logo(name='Dallas Stars', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/qrvywq1422042125.png', sport='Ice Hockey', shorthand='DAL', apisportsid=681, sportsdbid=134856, sportsipyid=None), + 'Detroit Red Wings': Logo(name='Detroit Red Wings', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/1c24ow1546544080.png', sport='Ice Hockey', shorthand='DET', apisportsid=682, sportsdbid=134832, sportsipyid=None), + 'Edmonton Oilers': Logo(name='Edmonton Oilers', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/uxxsyw1421618428.png', sport='Ice Hockey', shorthand='EDM', apisportsid=683, sportsdbid=134849, sportsipyid=None), + 'Florida Panthers': Logo(name='Florida Panthers', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/8qtaz11547158220.png', sport='Ice Hockey', shorthand='FLA', apisportsid=684, sportsdbid=134833, sportsipyid=None), + 'Los Angeles Kings': Logo(name='Los Angeles Kings', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/uvwtvx1421535024.png', sport='Ice Hockey', shorthand='LAK', apisportsid=685, sportsdbid=134852, sportsipyid=None), + 'Minnesota Wild': Logo(name='Minnesota Wild', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/swtsxs1422042685.png', sport='Ice Hockey', shorthand='MIN', apisportsid=687, sportsdbid=134857, sportsipyid=None), + 'Montreal Canadiens': Logo(name='Montreal Canadiens', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/stpryx1421791753.png', sport='Ice Hockey', shorthand='MTL', apisportsid=688, sportsdbid=134834, sportsipyid=None), + 'Nashville Predators': Logo(name='Nashville Predators', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/twqyvy1422052908.png', sport='Ice Hockey', shorthand='NSH', apisportsid=689, sportsdbid=134858, sportsipyid=None), + 'New Jersey Devils': Logo(name='New Jersey Devils', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/z4rsvp1619536740.png', sport='Ice Hockey', shorthand='NJD', apisportsid=690, sportsdbid=134840, sportsipyid=None), + 'New York Islanders': Logo(name='New York Islanders', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/hqn8511619536714.png', sport='Ice Hockey', shorthand='NYI', apisportsid=691, sportsdbid=134841, sportsipyid=None), + 'New York Rangers': Logo(name='New York Rangers', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/bez4251546192693.png', sport='Ice Hockey', shorthand='NYR', apisportsid=692, sportsdbid=134842, sportsipyid=None), + 'Ottawa Senators': Logo(name='Ottawa Senators', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/2tc1qy1619536592.png', sport='Ice Hockey', shorthand='OTT', apisportsid=693, sportsdbid=134835, sportsipyid=None), + 'Philadelphia Flyers': Logo(name='Philadelphia Flyers', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/qxxppp1421794965.png', sport='Ice Hockey', shorthand='PHI', apisportsid=695, sportsdbid=134843, sportsipyid=None), + 'Pittsburgh Penguins': Logo(name='Pittsburgh Penguins', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/dsj3on1546192477.png', sport='Ice Hockey', shorthand='PIT', apisportsid=696, sportsdbid=134844, sportsipyid=None), + 'San Jose Sharks': Logo(name='San Jose Sharks', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/yui7871546193006.png', sport='Ice Hockey', shorthand='SJS', apisportsid=697, sportsdbid=134853, sportsipyid=None), + 'Seattle Kraken': Logo(name='Seattle Kraken', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/zsx49m1595775836.png', sport='Ice Hockey', shorthand=None, apisportsid=1436, sportsdbid=140082, sportsipyid=None), + 'St. Louis Blues': Logo(name='St. Louis Blues', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/rsqtwx1422053715.png', sport='Ice Hockey', shorthand='STL', apisportsid=698, sportsdbid=134859, sportsipyid=None), + 'Tampa Bay Lightning': Logo(name='Tampa Bay Lightning', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/swysut1421791822.png', sport='Ice Hockey', shorthand='TBL', apisportsid=699, sportsdbid=134836, sportsipyid=None), + 'Toronto Maple Leafs': Logo(name='Toronto Maple Leafs', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/mxig4p1570129307.png', sport='Ice Hockey', shorthand='TOR', apisportsid=700, sportsdbid=134837, sportsipyid=None), + 'Vancouver Canucks': Logo(name='Vancouver Canucks', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/xqxxpw1421875519.png', sport='Ice Hockey', shorthand='VAN', apisportsid=701, sportsdbid=134850, sportsipyid=None), + 'Vegas Golden Knights': Logo(name='Vegas Golden Knights', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/7fd4521619536689.png', sport='Ice Hockey', shorthand='VGK', apisportsid=702, sportsdbid=135913, sportsipyid=None), + 'Washington Capitals': Logo(name='Washington Capitals', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/u17iel1547157581.png', sport='Ice Hockey', shorthand='WSH', apisportsid=703, sportsdbid=134845, sportsipyid=None), + 'Winnipeg Jets': Logo(name='Winnipeg Jets', sportsdb_leagueid=4380, url='https://www.thesportsdb.com/images/media/team/badge/bwn9hr1547233611.png', sport='Ice Hockey', shorthand='WPG', apisportsid=704, sportsdbid=134851, sportsipyid=None)} \ No newline at end of file diff --git a/lib/sports/sportbase.py b/lib/sports/sportbase.py index d476b20..42093d3 100644 --- a/lib/sports/sportbase.py +++ b/lib/sports/sportbase.py @@ -1,6 +1,257 @@ +from dataclasses import dataclass from lib.run import Runner, Caller +from typing import List, Tuple, Optional, Dict +from enum import Enum +from lib.sports.logo import Logo +from datetime import datetime + +class ModuleException(Exception): + pass + +class RequestException(ModuleException): + pass + +class API(Enum): + APISPORTS = 0 + SPORTSIPY = 1 + SPORTSDB = 2 + +class SportStructure(Enum): + Hockey = 0 + Baseball = 1 + Football = 2 + Basketball = 3 + +class GameStatus(Enum): + NotStarted = 0 + InGame = 1 + Finished = 2 + Overtime = 3 + +class GameResult(Enum): + WIN = 0 + LOSS = 1 + TIE = 2 + NOT_PLAYED = 3 class SportBase(Runner): pass + class SportResultBase(Caller): - pass \ No newline at end of file + pass + +@dataclass(repr=True) +class Team(): + name: str + logo: Logo + position: int = None + league: str = None + +@dataclass(repr=True) +class SportStandings(): + positions: List[Team] + +@dataclass(repr=True) +class Score: + team: int + opposing_team: int +@dataclass(repr=True) +class Game: + team: Team + timestamp: datetime + status: GameStatus + opposing_team: Team + result: GameResult + homeoraway: str + score: Score = None + +@dataclass(repr=True) +class BaseModuleResult(): + name: str + team: Dict[str, str] + schedule: Dict[str, str] + standings: Dict[str, str] + sport: SportStructure + +@dataclass(repr=True) +class ModuleResult(BaseModuleResult): + name: str + team: Team + schedule: List[Game] + standings: List[Team] + sport: SportStructure + games_played: int + wins: int + losses: int + +@dataclass(repr=True) +class HockeyResult(BaseModuleResult): + name: str + team: Team + schedule: List[Game] + standings: List[Team] + sport: SportStructure + games_played: int + wins: int + losses: int + +@dataclass(repr=True) +class FootballResult(BaseModuleResult): + name: str + team: Team + schedule: List[Game] + standings: List[Team] + sport: SportStructure + games_played: int + wins: int + losses: int + +@dataclass(repr=True) +class BaseballResult(BaseModuleResult): + name: str + team: Team + schedule: List[Game] + standings: List[Team] + sport: SportStructure + games_played: int + wins: int + losses: int + +def determine_game_status(team, game) -> Tuple[GameStatus, GameResult]: + if hasattr(game, "game"): + game_num = game.game + elif hasattr(game, "week"): + game_num = game.week + if hasattr(team, 'games_played'): + len_games = team.games_played + if hasattr(team, "games_finished"): + len_games = team.games_finished + if game_num == len_games: + return GameStatus.NotStarted, GameResult.NOT_PLAYED + elif game_num > len_games: + return GameStatus.NotStarted, GameResult.NOT_PLAYED + else: + if game.result == "Loss" or game.result == "OTL": + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.Finished, GameResult.WIN +def determine_team(game, team): + if game['teams']['home'] == team.name: + return True + return False + +def determine_apisports_game_status(game_dict: Dict, team, sport): + if sport == 'hockey': + in_game = ['P', 'OT', 'BT', "Q", "HT", "IN"] + if game_dict['status']['short'] in in_game: + return GameStatus.InGame, GameResult.NOT_PLAYED + elif game_dict['status']['short'] in ('FT', 'AP') and datetime.now().date() == datetime.fromtimestamp(game_dict['timestamp']).date(): + is_team_home_team = determine_team(game_dict, team) + if is_team_home_team: + if game_dict['scores']['home'] == game_dict['scores']['away']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home'] < game_dict['scores']['away']: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.Finished, GameResult.WIN + else: + if game_dict['scores']['home'] == game_dict['scores']['away']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home'] > game_dict['scores']['away']: + return GameStatus.Finished, GameResult.WIN + else: + return GameStatus.Finished, GameResult.LOSS + elif game_dict['status']['short'] in ('FT', 'AP') or datetime.fromtimestamp(game_dict['timestamp']) < datetime.now(): + is_team_home_team = determine_team(game_dict, team) + if is_team_home_team: + if game_dict['scores']['home'] == game_dict['scores']['away']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home'] < game_dict['scores']['away']: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.Finished, GameResult.WIN + else: + if game_dict['scores']['home'] == game_dict['scores']['away']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home'] > game_dict['scores']['away']: + return GameStatus.Finished, GameResult.WIN + else: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.NotStarted, GameResult.NOT_PLAYED + elif sport == 'basketball': + in_game = ['P', 'OT', 'BT', "Q", "HT", "IN"] + if game_dict['status']['short'] in in_game: + return GameStatus.InGame, GameResult.NOT_PLAYED + elif game_dict['status']['short'] in ('FT', 'AP') and datetime.now().date() == datetime.fromtimestamp(game_dict['timestamp']).date(): + is_team_home_team = determine_team(game_dict, team) + if is_team_home_team: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] < game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.Finished, GameResult.WIN + else: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] > game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.WIN + else: + return GameStatus.Finished, GameResult.LOSS + elif game_dict['status']['short'] in ('FT', 'AP') or datetime.fromtimestamp(game_dict['timestamp']) < datetime.now(): + is_team_home_team = determine_team(game_dict, team) + if is_team_home_team: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] < game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.Finished, GameResult.WIN + else: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] > game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.WIN + else: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.NotStarted, GameResult.NOT_PLAYED + elif sport == "baseball": + in_game = ['P', 'OT', 'BT', "Q", "HT", "IN"] + if game_dict['status']['short'] in in_game: + return GameStatus.InGame, GameResult.NOT_PLAYED + elif game_dict['status']['short'] in ('FT', 'AP') and datetime.now().date() == datetime.fromtimestamp(game_dict['timestamp']).date(): + is_team_home_team = determine_team(game_dict, team) + if is_team_home_team: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] < game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.Finished, GameResult.WIN + else: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] > game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.WIN + else: + return GameStatus.Finished, GameResult.LOSS + elif game_dict['status']['short'] in ('FT', 'AP') or datetime.fromtimestamp(game_dict['timestamp']) < datetime.now(): + is_team_home_team = determine_team(game_dict, team) + if is_team_home_team: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] < game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.Finished, GameResult.WIN + else: + if game_dict['scores']['home']['total'] == game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.TIE + elif game_dict['scores']['home']['total'] > game_dict['scores']['away']['total']: + return GameStatus.Finished, GameResult.WIN + else: + return GameStatus.Finished, GameResult.LOSS + else: + return GameStatus.NotStarted, GameResult.NOT_PLAYED \ No newline at end of file diff --git a/lib/sports/sports.py b/lib/sports/sports.py index 12a1322..43fd4a6 100755 --- a/lib/sports/sports.py +++ b/lib/sports/sports.py @@ -1,9 +1,16 @@ -from typing import List, Tuple, get_args +from statistics import mean from lib.sports.apisports.apisports import ApiSports +from lib.sports.sportsipy.sportsipy import SportsipyAPI +from sportsipy.nhl.teams import Team as nhl_team from lib.run import Runner, Caller -import os +from dataclasses import dataclass +from enum import Enum +from typing import Tuple, List, Dict, Set, Optional +from datetime import datetime +from lib.sports.sportbase import API +from lib.sports.logo import Logo, logo_map +import lib.sports.sportbase as base import json -import sys # TODO @thefinaljoke More abstract to use different apis class SportApi(Runner): @@ -23,82 +30,144 @@ async def run(self): api_sports = ApiSports(self.config) api_result = await api_sports.run_api_sports() return api_result + elif self.config['sport']['api'] or self.config['sport']['api'] == 'sportsipy': + sports = SportsipyAPI(self.config) + api_result = await sports.run_api_sportsipy() + return api_result return -class SportFinal(Caller): +@dataclass(repr=True) +class Sport: + team_name: str + # Enum Mapping to sport + sport: base.SportStructure + # Method of API + api: base.API + # Logo for the Team + logo: Logo + # List of standing in order + standings: base.SportStandings + # List of games + schedule: List[base.Game] + # A Single game representing the next game + next_game: base.Game + # A List of Wins, with a float of win percentage + wins: Tuple[List[base.Game], float] + # A List of Losses, with a flost of loss percentage + losses: Tuple[List[base.Game], float] + # Set of Leagues if Applicable + leagues: Set[str] = None + +class SportTransform(Caller): """ Final Normalized Object that goes to - The Sport Matrix. + The Sport Matrix. This is the final object + * NO MATTER What the return types should not change + """ def __init__(self, api_result) -> None: # Any Object from any api object self.api_result = api_result + + @property + def team_name(self): + return self.api_result.team_name @property - def get_sport(self): + def get_api(self) -> Enum: + return self.api_result.get_api + + def _normalize_sport(self) -> Enum: + """ + All apis Should return a string in + thier result object + """ return self.api_result.get_sport + + @property + def get_sport(self) -> Enum: + return self._normalize_sport() + + def _normalize_logo(self) -> Logo: + return self.api_result.get_logo + @property - def get_error(self): - return self.api_result.get_error + def get_logo(self) -> Logo: + return self._normalize_logo() + + def _normalize_length_position_teams(self) -> int: + return self.api_result.get_length_position_teams + @property def get_length_position_teams(self): - return len(self.api_result.standings) + return self._normalize_length_position_teams() + + def _normalize_standings(self) -> base.SportStandings: + return self.api_result.get_standings @property def get_standings(self): - return self.api_result.standings - - @property - def position_teams(self): - return self.api_result.position_teams + return self._normalize_standings() - @property - def get_leagues(self): + def _normalize_leagues(self) -> Set[str]: return self.api_result.get_leagues @property - def get_games_played(self): - return self.api_result.games_played - + def get_leagues(self) -> Set[str]: + return self._normalize_leagues() + + def _normalize_schedule(self): + return self.api_result.get_schedule + @property - def get_wins(self): - return self.api_result.get_wins + def get_schedule(self): + return self._normalize_schedule() + + def _normalize_games_played(self) -> List[str]: + return self.api_result.get_games_played @property - def get_wins_percentage(self): - return self.api_result.win_percentage + def get_games_played(self) -> List[str]: + return self._normalize_games_played() - @property - def get_losses(self): - return self.api_result.losses + def _normalize_next_game(self) -> base.Game: + return self.api_result.get_next_game @property - def get_loss_percentage(self): - return self.api_result.loss_percentage + def get_next_game(self) -> base.Game: + return self._normalize_next_game() - @property - def get_game_ids(self): - return self.api_result.game_ids - + def _normalize_wins(self) -> List[str]: + return self.api_result.get_wins + @property - def get_timestamps(self): - return self.api_result.timestamps + def get_wins(self) -> List[str]: + return self._normalize_wins() + def _normalize_win_percentage(self) -> float: + return self.api_result.get_wins_percentage + @property - def get_teams(self): - return self.api_result.teams + def get_wins_percentage(self) -> float: + return self._normalize_win_percentage() - @property - def get_versus(self): - return self.api_result.vs + def _normalize_losses(self) -> List[str]: + return self.api_result.get_losses @property - def get_status(self): - return self.api_result.status + def get_losses(self) -> List[str]: + return self._normalize_losses() + def _normalize_loss_percentage(self) -> float: + return self.api_result.get_loss_percentage + @property - def get_scores(self): - return self.api_result.game_result - - def get_specific_score(self, game_id): - return self.api_result.game_result.get(game_id) \ No newline at end of file + def get_loss_percentage(self) -> float: + return self._normalize_loss_percentage() + + def _normalize_game_ids(self) -> Optional[List[int]]: + return self.api_result.get_game_ids + + @property + def get_game_ids(self) -> Optional[List[int]]: + return self._normalize_game_ids() \ No newline at end of file diff --git a/lib/sports/sportsipy/__init__.py b/lib/sports/sportsipy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/sports/sportsipy/baseball/__init__.py b/lib/sports/sportsipy/baseball/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/sports/sportsipy/baseball/baseball.py b/lib/sports/sportsipy/baseball/baseball.py new file mode 100644 index 0000000..7eaae4d --- /dev/null +++ b/lib/sports/sportsipy/baseball/baseball.py @@ -0,0 +1,93 @@ +import asyncio +from lib.sports.sportsipy.result import SportsipyApiResult +from sportsipy.mlb.schedule import Schedule, Game +from sportsipy.mlb.teams import ( + Team, + Teams, +) +from typing import List +from lib.sports.logo import logo_map, baseball_teams +from sportsipy.mlb.boxscore import Boxscore, Boxscores +from lib.asynclib import make_async +from datetime import datetime +from lib.run import Runner +import lib.sports.sportbase as base + + +class BaseballSportsipy(Runner): + def __init__(self, config): + super().__init__(config) + + + @make_async + def run_team(self, team: str) -> Team: + self.logger.debug("Running Team") + return Team(team) + + @make_async + def run_schedule(self, team: str) -> Schedule: + self.logger.debug("Running Schedule") + return Schedule(team) + + @make_async + def run_standings(self): + self.logger.debug("Running Standings") + return Teams() + + async def run(self) -> SportsipyApiResult: + try: + self.logger.info('Running Sportsipy') + sport = {} + team = self.config['sport']['team_id'] + self.logger.info("Running Baseball Sportsipy Api") + sport['team'] = asyncio.create_task(self.run_team(team), name="team_task") + sport['schedule'] = asyncio.create_task(self.run_schedule(team), name="schedule_task") + sport['standings'] = asyncio.create_task(self.run_standings(), name="standing_task") + await asyncio.gather(*sport.values()) + sport['sport'] = base.SportStructure.Baseball + abbr = sport['team'].result().abbreviation + baseball_result = base.ModuleResult( + name=baseball_teams[abbr], + team=base.Team( + name=baseball_teams[abbr], + logo=logo_map[baseball_teams[abbr]], + position=sport['team'].result().rank + ), + schedule=[ + base.Game( + team=base.Team( + name=baseball_teams[abbr], + logo=logo_map[baseball_teams[abbr]], + position=sport['team'].result().rank + ), + timestamp=game.datetime, + status=base.determine_game_status(sport['team'].result(), game)[0], + opposing_team=base.Team( + name=baseball_teams[game.opponent_abbr], + logo=logo_map[baseball_teams[game.opponent_abbr]], + ), + result=base.determine_game_status(sport['team'].result(), game)[1], + homeoraway=game.location, + score=base.Score( + team=game.runs_scored, + opposing_team=game.runs_allowed + ) + ) for game in sport['schedule'].result() + ], + standings=[ + base.Team( + name=baseball_teams[team.abbreviation], + logo=logo_map[baseball_teams[team.abbreviation]], + position=team.rank + ) for team in sport['standings'].result() + ], + sport=base.SportStructure.Baseball, + games_played=sport['team'].result().games_finished, + wins=sport['team'].result().wins, + losses=sport['team'].result().losses + ) + return SportsipyApiResult(api_result=baseball_result) + except Exception as error: + self.logger.error(f"Error Occured inside of baseball module: {error}") + return None + diff --git a/lib/sports/sportsipy/basketball/__init__.py b/lib/sports/sportsipy/basketball/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/sports/sportsipy/basketball/basketball.py b/lib/sports/sportsipy/basketball/basketball.py new file mode 100644 index 0000000..9af75e4 --- /dev/null +++ b/lib/sports/sportsipy/basketball/basketball.py @@ -0,0 +1,94 @@ +import asyncio +from lib.sports.sportsipy.result import SportsipyApiResult +from sportsipy.nba.schedule import Schedule, Game +from sportsipy.nba.teams import ( + Team, + Teams, +) +from typing import List +from sportsipy.nba.boxscore import Boxscore, Boxscores +from lib.asynclib import make_async +from datetime import datetime +from lib.run import Runner +import lib.sports.sportbase as base + +class BasketballSportsipy(Runner): + """ + Right now of 11-14-21 + There is a failure inside of the basketball + SportsipyApi Teams() Module + """ + + def __init__(self, config): + super().__init__(config) + + @make_async + def run_team(self, team: str) -> Team: + self.logger.debug("Running Team") + return Team(team) + + @make_async + def run_schedule(self, team: str) -> Schedule: + self.logger.debug("Running Schedule") + return Schedule(team) + + @make_async + def run_standings(self): + self.logger.debug("Running Standings") + return Teams() + + async def run(self) -> SportsipyApiResult: + try: + self.logger.info('Running Sportsipy') + sport = {} + team = self.config['sport']['team_id'] + self.logger.info("Running Basketball Sportsipy Api") + sport['team'] = asyncio.create_task(self.run_team(team), name="team_task") + sport['schedule'] = asyncio.create_task(self.run_schedule(team), name="schedule_task") + sport['standings'] = asyncio.create_task(self.run_standings(), name="standing_task") + await asyncio.gather(*sport.values()) + sport['sport'] = base.SportStructure.Basketball + basketball_result = base.ModuleResult( + name=sport['team'].result().name, + team=base.Team( + name=sport['team'].result().name, + logo=logo_map[sport['team'].result().name], + position=sport['team'].result().rank + ), + schedule=[ + base.Game( + team = base.Team( + name=sport['team'].result().name, + logo=logo_map[sport['team'].result().name], + position=sport['team'].result().rank + ), + timestamp=game.datetime, + status=base.determine_game_status(sport['team'].result(), game)[0], + opposing_team= base.Team( + name=game.opponent_name, + logo=logo_map[game.opponent_name] + ), + result=base.determine_game_status(sport['team'].result(), game)[1], + homeoraway=game.location, + score=base.Score( + team=game.goals_scored, + opposing_team=game.goals_allowed + ) if base.GameStatus.Finished else None + ) for game in sport['schedule'].result() + ], + standings=[ + base.Team( + name=team.name, + logo=logo_map[team.name], + position=team.rank + ) for team in sport['standings'].result() + ], + sport=sport['sport'], + games_played=sport['team'].result().games_played, + wins=sport['team'].result().wins, + losses=sport['team'].result().losses + ) + return SportsipyApiResult(api_result=basketball_result) + except Exception as error: + self.logger.error(f"An Error Occured in basketball Module: {error}") + return None diff --git a/lib/sports/sportsipy/football/__init__.py b/lib/sports/sportsipy/football/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/sports/sportsipy/football/football.py b/lib/sports/sportsipy/football/football.py new file mode 100644 index 0000000..2c902eb --- /dev/null +++ b/lib/sports/sportsipy/football/football.py @@ -0,0 +1,90 @@ +import asyncio +import lib.sports.sportbase as base +from lib.sports.sportsipy.result import SportsipyApiResult +from sportsipy.nfl.schedule import Schedule, Game +from sportsipy.nfl.teams import ( + Team, + Teams, +) +from typing import List +from lib.sports.logo import logo_map +from lib.asynclib import make_async +from datetime import datetime +from lib.run import Runner + +class FootballSportsipy(Runner): + def __init__(self, config): + super().__init__(config) + + @make_async + def run_team(self, team: str) -> Team: + self.logger.debug("Running Team") + return Team(team) + + @make_async + def run_schedule(self, team: str) -> Schedule: + self.logger.debug("Running Schedule") + return Schedule(team) + + @make_async + def run_standings(self): + self.logger.debug("Running Standings") + return Teams() + + async def run(self) -> SportsipyApiResult: + try: + self.logger.info("Inside of the Football Sportsipy") + sport = {} + team = self.config['sport']['team_id'] + self.logger.info("Running Football Sportsipy Api") + sport['team'] = asyncio.create_task(self.run_team(team), name="team_task") + sport['schedule'] = asyncio.create_task(self.run_schedule(team), name="schedule_task") + sport['standings'] = asyncio.create_task(self.run_standings(), name="standing_task") + await asyncio.gather(*sport.values()) + sport['sport'] = base.SportStructure.Football + football_result = base.ModuleResult( + name=sport['team'].result().name, + team=base.Team( + name=sport['team'].result().name, + logo=logo_map[sport['team'].result().name], + position=sport['team'].result().rank + ), + schedule=[ + base.Game( + team = base.Team( + name=sport['team'].result().name, + logo=logo_map[sport['team'].result().name], + position=sport['team'].result().rank + ), + timestamp=game.datetime, + status=base.determine_game_status(sport['team'].result(), game)[0], + opposing_team= base.Team( + name=game.opponent_name, + logo=logo_map[game.opponent_name] + ), + result=base.determine_game_status(sport['team'].result(), game)[1], + homeoraway=game.location, + score=base.Score( + team=game.points_scored, + opposing_team=game.points_allowed + ) if base.GameStatus.Finished else None + ) for game in sport['schedule'].result() + ], + standings=[ + base.Team( + name=team.name, + logo=logo_map[team.name], + position=team.rank + ) for team in sport['standings'].result() + ], + sport=sport['sport'], + games_played=sport['team'].result().games_played, + wins=sport['team'].result().wins, + losses=sport['team'].result().losses + ) + return SportsipyApiResult(api_result=football_result) + except Exception as error: + self.logger.error(f"Error Occured inside of football module: {error}") + return None + + diff --git a/lib/sports/sportsipy/hockey/__init__.py b/lib/sports/sportsipy/hockey/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/sports/sportsipy/hockey/hockey.py b/lib/sports/sportsipy/hockey/hockey.py new file mode 100644 index 0000000..8336afb --- /dev/null +++ b/lib/sports/sportsipy/hockey/hockey.py @@ -0,0 +1,91 @@ +import asyncio +from asyncio.tasks import Task +from lib.sports.logo import Logo, logo_map +from lib.sports.sportsipy.result import SportsipyApiResult +from sportsipy.nhl.schedule import Schedule, Game +from sportsipy.nhl.teams import ( + Team, + Teams, +) +from typing import List, Tuple +from sportsipy.nhl.boxscore import Boxscore, Boxscores +from lib.asynclib import make_async +from datetime import datetime +from lib.run import Runner +import lib.sports.sportbase as base + +class HockeySportsipy(Runner): + def __init__(self, config): + super().__init__(config) + + @make_async + def run_team(self, team: str) -> Team: + self.logger.debug("Running Team") + return Team(team) + + @make_async + def run_schedule(self, team: str) -> Schedule: + self.logger.debug("Running Schedule") + return Schedule(team) + + @make_async + def run_standings(self): + self.logger.debug("Running Standings") + return Teams() + + async def run(self) -> SportsipyApiResult: + try: + self.logger.info('Running Sportsipy') + sport = {} + team = self.config['sport']['team_id'] + self.logger.info("Running Hockey Sportsipy Api") + sport['team'] = asyncio.create_task(self.run_team(team), name="team_task") + sport['schedule'] = asyncio.create_task(self.run_schedule(team), name="schedule_task") + sport['standings'] = asyncio.create_task(self.run_standings(), name="standing_task") + await asyncio.gather(*sport.values()) + sport['sport'] = base.SportStructure.Hockey + hockey_result = base.ModuleResult( + name=sport['team'].result().name, + team=base.Team( + name=sport['team'].result().name, + logo=logo_map[sport['team'].result().name], + position=sport['team'].result().rank + ), + schedule=[ + base.Game( + team = base.Team( + name=sport['team'].result().name, + logo=logo_map[sport['team'].result().name], + position=sport['team'].result().rank + ), + timestamp=game.datetime, + status=base.determine_game_status(sport['team'].result(), game)[0], + opposing_team= base.Team( + name=game.opponent_name, + logo=logo_map[game.opponent_name] + ), + result=base.determine_game_status(sport['team'].result(), game)[1], + homeoraway=game.location, + score=base.Score( + team=game.goals_scored, + opposing_team=game.goals_allowed + ) if base.GameStatus.Finished else None + ) for game in sport['schedule'].result() + ], + standings=[ + base.Team( + name=team.name, + logo=logo_map[team.name], + position=team.rank + ) for team in sport['standings'].result() + ], + sport=sport['sport'], + games_played=sport['team'].result().games_played, + wins=sport['team'].result().wins, + losses=sport['team'].result().losses + ) + return SportsipyApiResult(api_result=hockey_result) + except Exception as error: + self.logger.error(f"An Error Occured in basketball Module: {error}") + return None + diff --git a/lib/sports/sportsipy/result.py b/lib/sports/sportsipy/result.py new file mode 100644 index 0000000..9455bb5 --- /dev/null +++ b/lib/sports/sportsipy/result.py @@ -0,0 +1,99 @@ +import json +from asyncio import Task +from typing import Dict, List, Tuple +from datetime import datetime +from enum import Enum +from sportsipy.nhl.schedule import Game +from sportsipy.nhl.teams import Team as nhl_team +from lib.sports.sportbase import SportResultBase, API +from lib.sports.logo import Logo, logo_map +import lib.sports.sportbase as base + +class SportsipyApiResult(SportResultBase): + + def __init__(self, api_result: Dict[str, Task]) -> None: + self.api_result = api_result + self._get_sport: Enum = api_result.sport + self._team: base.Team = api_result.team + self._schedule: base.SportStandings = base.SportStandings( + positions=api_result.schedule + ) + + self._api: Enum = API.SPORTSIPY + self._standings: List[base.Team] = api_result.standings + self._position = self._team.position + self._get_leagues = None + self._games_played: List[Game] = self._schedule.positions[:api_result.games_played] + self._get_wins: List[Game] = [game for game in self._games_played if base.GameResult.WIN == game.result] + self._win_percentage: float = api_result.wins/len(self._games_played) + self._losses: List[Game] = [game for game in self._games_played if base.GameResult.LOSS == game.result] + self._loss_percentage: float = api_result.losses/len(self._games_played) + self._next_game = self._schedule.positions[len(self._games_played)] + self._game_ids = None + + @property + def get_api(self) -> Enum: + return self._api + + @property + def get_sport(self) -> Enum: + return self._get_sport + + @property + def team_name(self): + return self._team.name + + @property + def get_logo(self) -> Logo: + return logo_map[self._team.name] + + @property + def get_team(self): + return self._team + + @property + def get_length_position_teams(self): + return len(self._standings) + + @property + def get_standings(self): + return self._standings + + @property + def get_schedule(self): + return self._schedule + + @property + def get_leagues(self): + return self._get_leagues + + @property + def get_games_played(self): + return self._games_played + + @property + def get_wins(self): + return self._get_wins + + @property + def get_wins_percentage(self): + return self._win_percentage + + @property + def get_losses(self): + return self._losses + + @property + def get_loss_percentage(self): + return self._loss_percentage + + @property + def get_game_ids(self): + return self._game_ids + + def get_specific_score(self, game_id): + return self._game_result.get(game_id) + + @property + def get_next_game(self): + return self._next_game \ No newline at end of file diff --git a/lib/sports/sportsipy/sportsipy.py b/lib/sports/sportsipy/sportsipy.py new file mode 100644 index 0000000..524be49 --- /dev/null +++ b/lib/sports/sportsipy/sportsipy.py @@ -0,0 +1,28 @@ +from lib.sports.sportbase import SportBase, SportResultBase +from lib.sports.sportsipy.hockey.hockey import HockeySportsipy +from lib.sports.sportsipy.baseball.baseball import BaseballSportsipy +from lib.sports.sportsipy.basketball.basketball import BasketballSportsipy +from lib.sports.sportsipy.football.football import FootballSportsipy + +class SportsipyAPI(SportBase): + def __init__(self, config): + self.config = config + self.sport = self.config['sport'] + + async def run_api_sportsipy(self): + if 'hockey' == self.sport.get('sport').lower(): + self.logger.debug("Running hockey sportsipy") + hockey = HockeySportsipy(self.config) + return await hockey.run() + elif 'baseball' == self.sport.get('sport').lower(): + self.logger.debug("Running Baseball sportsipy") + baseball = BaseballSportsipy(self.config) + return await baseball.run() + elif 'basketball' == self.sport.get('sport').lower(): + self.logger.debug("Running basketball sportsipy") + basketball = BasketballSportsipy(self.config) + return await basketball.run() + elif 'football' == self.sport.get('sport').lower(): + self.logger.debug("Running football sportsipy") + football = FootballSportsipy(self.config) + return await football.run() \ No newline at end of file diff --git a/matrix/sport/sportmatrix.py b/matrix/sport/sportmatrix.py index aceb5df..a9fd461 100755 --- a/matrix/sport/sportmatrix.py +++ b/matrix/sport/sportmatrix.py @@ -4,13 +4,14 @@ import os import time import urllib.request +import lib.sports.sportbase as sport_types from datetime import datetime -from collections import defaultdict -from typing import List, Dict, Tuple -from collections import deque +from typing import Dict, Tuple + +from sportsipy.nhl.schedule import Schedule from matrix.error import ErrorMatrix from PIL import ImageFont, Image, ImageDraw -from lib.sports.sports import SportFinal +from lib.sports.sports import SportTransform, Sport from matrix.matrix import Matrix class SportMatrix(Matrix): @@ -18,52 +19,52 @@ def __init__(self, matrix, api, logger: Logger) -> None: self.matrix = matrix self.api = api self.logger = logger + def __str__(self) -> str: return "SportMatrix" - async def poll_api(self) -> SportFinal: - return SportFinal(await self.api.run()) - def divisions(self, standings: List[Dict]) -> Tuple[List[str], List[str]]: - leagues = {league['league']: [] for league in standings} - for team in standings: - leagues[team['league']].append(f"{team['position']}: {team['name']}") - return leagues - def determine_nextgame(self, nextgame_api): - status: Tuple = ("FT", "ABD") - for game in nextgame_api: - if "IN" in game['status']: - self.logger.debug(f"In Game") - # During the game - return game - if game['status'] == "FT" and datetime.fromtimestamp(game['timestamp']).date() == datetime.today().date(): - # Same Day will display for the rest of the day - self.logger.debug("Game is finished but still same day") - return game - if game['status'] not in status: - return game + async def poll_api(self) -> Sport: + sport = SportTransform(await self.api.run()) + if not sport.api_result: + return None + return Sport( + team_name=sport.team_name, + sport=sport.get_sport, + logo=sport.get_logo, + api=sport.get_api, + standings=sport.get_standings, + schedule=sport.get_schedule, + next_game=sport.get_next_game, + wins=sport.get_wins, + losses=sport.get_losses, + ) def away_team(self, nextgame_data): - away = nextgame_data['teams']['away'] - away.update(nextgame_data['score']['away']) - return away + if nextgame_data.homeoraway.lower() == "home": + return nextgame_data.team + else: + return nextgame_data.opposing_team + def home_team(self, nextgame_data): - home = nextgame_data['teams']['home'] - home.update(nextgame_data['score']['home']) - return home + if nextgame_data.homeoraway.lower() == "away": + return nextgame_data.team + else: + return nextgame_data.opposing_team def get_logo(self, logo_url: str, name: str) -> str: + name = name.replace(" ", "_") file_name: str = f"/tmp/{name}.png" if not os.path.isfile(file_name): urllib.request.urlretrieve(logo_url, file_name) return file_name - def build_in_game_image(self, nextgame: Dict): + def build_in_game_image(self, nextgame): middle_image = self.make_new_image((34,16)) middle_draw = ImageDraw.Draw(middle_image) font = ImageFont.truetype("/usr/share/fonts/fonts/04B_03B_.TTF", 8) - status = nextgame['status'] + status = nextgame.status self.logger.debug(f"status: {status}") - score = (nextgame['teams']['home']['total'], nextgame['teams']['away']['total']) + score = (nextgame.score.team, nextgame.score.opposing_team) self.logger.debug(f"Score: {score}") middle_draw.multiline_text((12,0), f"{status}\n{score[0]}-{score[1]}", font=font) return middle_image, (15, 0) @@ -72,57 +73,49 @@ def build_finished_game_image(self, nextgame): middle_image = self.make_new_image((34,16)) middle_draw = ImageDraw.Draw(middle_image) font = ImageFont.truetype("/usr/share/fonts/fonts/04B_03B_.TTF", 8) - status = nextgame['status'] - score = (nextgame['teams']['home']['total'], nextgame['teams']['away']['total']) + status = nextgame.status + score = (nextgame.score.team, nextgame.score.opposing_team) middle_draw.multiline_text((12,0), f"{status}\n{score[0]}-{score[1]}", font=font) return middle_image, (15, 0) def check_offseason(self, api) -> bool: try: - start_time, end_time = api.get_timestamps[0][1], api.get_timestamps[-1][1] - if datetime.fromtimestamp(start_time) <= datetime.now() <= datetime.fromtimestamp(end_time): + start_time, end_time = api.schedule.positions[0].timestamp, api.schedule.positions[-1].timestamp + if start_time <= datetime.now() <= end_time: return True except Exception: return False - def build_next_game_image(self, nextgame: Dict): + def build_next_game_image(self, nextgame): middle_image = self.make_new_image((34,16)) middle_draw = ImageDraw.Draw(middle_image) font = ImageFont.truetype("/usr/share/fonts/fonts/04B_03B_.TTF", 8) - time = datetime.fromtimestamp(nextgame['timestamp']).strftime("%I:%M%p %a") + time = nextgame.timestamp.strftime("%m-%d %a") formatted_time = time.split() - formatted_time[0] = f" {formatted_time[0]}" + formatted_time[0] = f" {formatted_time[0]}" time = "\n ".join(formatted_time) middle_draw.multiline_text((0,0), f'{time}', font=font) return middle_image, (15, 0) def build_home_away_image(self, nextgame): - self.logger.debug(f"Building Home away image") home_data = self.home_team(nextgame) - self.logger.debug(f"Home Data {home_data}") - home_logo = Image.open(self.get_logo(home_data['logo'], home_data['id'])) - self.logger.debug(f"Got Logo {home_logo}") + home_logo = Image.open(self.get_logo(home_data.logo.url, home_data.name)) home_logo.thumbnail((16,16)) away_data = self.away_team(nextgame) - self.logger.debug(f"Away Data: {away_data}") - away_logo = Image.open(self.get_logo(away_data['logo'], away_data['id'])) - self.logger.debug(f"Away Logo {away_logo}") + away_logo = Image.open(self.get_logo(away_data.logo.url, away_data.name)) away_logo.thumbnail((16,16)) return (home_logo, (-2,0)), (away_logo, (50, 0)) def build_middle_nextgame(self, api) -> Image: - nextgame = self.determine_nextgame(api.next_game) - in_game_status = ("IN", "Q", "OT", "BT", "HT", "P") - if nextgame['status'] in in_game_status: - return self.build_in_game_image(nextgame) - elif "NS" == nextgame['status']: - return self.build_next_game_image(nextgame) - elif "FT" == nextgame['status']: - return self.build_finished_game_image(nextgame) + if api.next_game.status == sport_types.GameStatus.InGame: + return self.build_in_game_image(api.next_game) + elif sport_types.GameStatus.NotStarted == api.next_game.status: + return self.build_next_game_image(api.next_game) + elif sport_types.GameStatus.Finished == api.next_game.status: + return self.build_finished_game_image(api.next_game) def build_middle_image(self, api) -> Image: - nextgame = self.determine_nextgame(api.next_game) - home_image, away_image = self.build_home_away_image(nextgame) + home_image, away_image = self.build_home_away_image(api.next_game) middle_image = self.build_middle_nextgame(api) master_middle_image = self.make_new_image((64, 16)) master_middle_image.paste(home_image[0], home_image[1]) @@ -136,16 +129,15 @@ def build_top_home_away_images(self, nextgame: Dict, xpos: int) -> Tuple: top_home_draw = ImageDraw.Draw(top_home_image) top_away_image = self.make_new_image((22,8)) top_away_draw = ImageDraw.Draw(top_away_image) - hometeam = nextgame['teams']['home']['name'] - awayteam = nextgame['teams']['away']['name'] + hometeam = nextgame.team.name + awayteam = nextgame.opposing_team.name top_home_draw.text((-xpos,0), hometeam, font=font) top_away_draw.text((-xpos,0), awayteam, font=font) return top_home_image, top_away_image def build_top_image(self, api: Dict, xpos: int) -> Tuple: - nextgame = self.determine_nextgame(api.next_game) master_top_image = self.make_new_image((64, 8)) - home_image, away_image = self.build_top_home_away_images(nextgame, xpos) + home_image, away_image = self.build_top_home_away_images(api.next_game, xpos) top_middle_image = self.make_new_image((22,8)) top_middle_draw = ImageDraw.Draw(top_middle_image) top_middle_draw.text((5,0), "VS", font=ImageFont.truetype("/usr/share/fonts/fonts/04B_03B_.TTF", 8)) @@ -162,12 +154,8 @@ def build_standings_image(self, api, xpos) -> Tuple[int, int]: standings_draw = ImageDraw.Draw(standings_image) scrolling_font = ImageFont.truetype("/usr/share/fonts/fonts/04B_03B_.TTF", 8) color = (156,163,173) - # Can't Have multiple images and or buffers - divs = self.divisions(api.get_standings) - master = [] - for league, names in divs.items(): - master.append(f"{league} " + " ".join(names)) - text = " ".join(master) + + text = " ".join(api) standings_draw.text( (-xpos, 0), text, @@ -178,89 +166,36 @@ def build_standings_image(self, api, xpos) -> Tuple[int, int]: return standings_image, (0, 25) async def render(self, api, loop): - try: + if not api: + raise Exception("Error Occurrred inside of the sport matrix") self.clear() self.reload_image() - if not api.get_error[0]: - raise Exception(api.get_error) - if 'baseball' in api.get_sport: - self.logger.info("Found Baseball, Displaying Baseball Matrix") - if self.check_offseason(api): - xpos = 0 - xpos_for_top = 0 - while xpos < 2700: - self.reload_image() - images = ( - self.build_standings_image(api, xpos), - self.build_middle_image(api), - self.build_top_image(api, xpos_for_top), - ) - for image, position in images: - self.paste_image(image, position) - await self.render_image() - xpos +=1 - xpos_for_top += 1 - if xpos_for_top == 100: - xpos_for_top = 0 - time.sleep(3) if xpos == 1 else time.sleep(.001) - else: - font = ImageFont.truetype("/usr/share/fonts/fonts/04b24.otf", 14) - self.draw_multiline_text((0, 0), "Baseball\nOffseason", font=font) - await self.render_image() - time.sleep(30) - - if 'basketball' in api.get_sport: - self.logger.info("Found Basketball, Displaying Basketball Matrix") - if self.check_offseason(api): - xpos = 0 - xpos_for_top = 0 - while xpos < 2700: - self.reload_image() - images = ( - self.build_standings_image(api, xpos), - self.build_middle_image(api), - self.build_top_image(api, xpos_for_top), - ) - for image, position in images: - self.paste_image(image, position) - self.render_image() - xpos +=1 - xpos_for_top += 1 - if xpos_for_top == 100: - xpos_for_top = 0 - time.sleep(3) if xpos == 1 else time.sleep(.01) - else: - font = ImageFont.truetype("/usr/share/fonts/fonts/04b24.otf", 14) - self.draw_multiline_text((0, 0), "Basketball\nOffseason", font=font) + if self.check_offseason(api): + xpos = 0 + xpos_for_top = 0 + positions = [f"{team.position}. {team.name}" for team in api.standings] + while xpos < 2700: + self.reload_image() + images = ( + self.build_standings_image(positions, xpos), + self.build_middle_image(api), + self.build_top_image(api, xpos_for_top), + ) + for image, position in images: + self.paste_image(image, position) await self.render_image() - time.sleep(30) + xpos +=1 + xpos_for_top += 1 + if xpos_for_top == 100: + xpos_for_top = 0 + time.sleep(3) if xpos == 1 else time.sleep(.001) + else: + font = ImageFont.truetype("/usr/share/fonts/fonts/04b24.otf", 14) + self.draw_multiline_text((0, 0), "Baseball\nOffseason", font=font) + await self.render_image() + time.sleep(30) - if 'hockey' in api.get_sport: - self.logger.info("Found Hockey, Displaying Hockey Matrix") - if self.check_offseason(api): - xpos = 0 - xpos_for_top = 0 - while xpos < 2700: - self.reload_image() - images = ( - self.build_standings_image(api, xpos), - self.build_middle_image(api), - self.build_top_image(api, xpos_for_top), - ) - for image, position in images: - self.paste_image(image, position) - self.render_image() - xpos +=1 - xpos_for_top += 1 - if xpos_for_top == 100: - xpos_for_top = 0 - time.sleep(3) if xpos == 1 else time.sleep(.01) - else: - font = ImageFont.truetype("/usr/share/fonts/fonts/04b24.otf", 14) - self.draw_multiline_text((0, 0), "Hockey\nOffseason", font=font) - await self.render_image() - time.sleep(30) except Exception as e: self.logger.error(e) error_matrix = ErrorMatrix(self.matrix, self.logger, "Sports Matrix")