diff --git a/lib/sports/baseball/__init__.py b/lib/sports/apisports/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from lib/sports/baseball/__init__.py rename to lib/sports/apisports/__init__.py diff --git a/lib/sports/apisports/apisports.py b/lib/sports/apisports/apisports.py new file mode 100644 index 0000000..201d435 --- /dev/null +++ b/lib/sports/apisports/apisports.py @@ -0,0 +1,44 @@ +from lib.sports.sportbase import SportBase +from lib.sports.apisports.baseball.baseball import Baseball +from lib.sports.apisports.basketball.basketball import Basketball +from lib.sports.apisports.hockey.hockey import Hockey +from lib.sports.apisports.football.football import Football +import json +import sys +import os + +class ApiSports(SportBase): + def __init__(self, config): + self.config = config + self.sport = self.config['sport'] + try: + if "sport_token" in self.config['basic']: + self.token = self.config['basic'].get('sport_token') + else: + self.token = os.environ['SPORTTOKEN'] + except KeyError: + self.logger.critical("No Sport Token") + sys.exit("No Sport Token") + self.headers = {'x-apisports-key': 'ebb2c44c416b9a9a0b538e2d73c7dbe6'} + + async def run_api_sports(self): + sport_data = {"Sport": {}} + if 'football' == self.sport.get('sport').lower(): + self.logger.debug("Running football data") + football = Football(self.token, self.config['sport'], self.headers) + football_return = await football.run() + sport_data['Sport'].update({'football': football_return}) + elif 'baseball' == self.sport.get('sport').lower(): + self.logger.debug('Running baseball data') + baseball = Baseball(self.token, self.config['sport'], self.headers) + sport_data = await baseball.run() + elif 'basketball' == self.sport.get('sport').lower(): + self.logger.debug('Got basketball in config') + basketball = Basketball(self.token, self.config['sport'], self.headers) + basketball_return = await basketball.run() + sport_data['Sport'].update({'basketball': basketball_return}) + elif 'hockey' == self.sport.get('sport').lower(): + self.logger.debug('Got Hockey from Config') + hockey = Hockey(self.token, self.config['sport'], self.headers) + sport_data = await hockey.run() + return sport_data \ No newline at end of file diff --git a/lib/sports/basketball/__init__.py b/lib/sports/apisports/baseball/__init__.py similarity index 100% rename from lib/sports/basketball/__init__.py rename to lib/sports/apisports/baseball/__init__.py diff --git a/lib/sports/baseball/baseball.py b/lib/sports/apisports/baseball/baseball.py similarity index 90% rename from lib/sports/baseball/baseball.py rename to lib/sports/apisports/baseball/baseball.py index b2ba589..0386edc 100755 --- a/lib/sports/baseball/baseball.py +++ b/lib/sports/apisports/baseball/baseball.py @@ -1,5 +1,6 @@ from lib.run import Runner from datetime import datetime +from lib.sports.apisports.result import SportApiResult class Baseball(Runner): def __init__(self, token, config, headers): @@ -33,4 +34,5 @@ async def run(self): api_data = {} for section, url in self.url_builder(parsed).items(): api_data.update({section: await self.get_data(url, self.headers)}) - return api_data + api_data['sport'] = 'baseball' + return SportApiResult(api_data) diff --git a/lib/sports/hockey/__init__.py b/lib/sports/apisports/basketball/__init__.py similarity index 100% rename from lib/sports/hockey/__init__.py rename to lib/sports/apisports/basketball/__init__.py diff --git a/lib/sports/basketball/basketball.py b/lib/sports/apisports/basketball/basketball.py similarity index 97% rename from lib/sports/basketball/basketball.py rename to lib/sports/apisports/basketball/basketball.py index f08a2f8..8123b93 100755 --- a/lib/sports/basketball/basketball.py +++ b/lib/sports/apisports/basketball/basketball.py @@ -35,4 +35,5 @@ async def run(self): api_data = {} for section, url in self.url_builder(parsed).items(): api_data.update({section: await self.get_data(url, self.headers)}) + api_data['sport'] = 'basketball' return api_data \ No newline at end of file diff --git a/lib/sports/apisports/football/__init__.py b/lib/sports/apisports/football/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/sports/football.py b/lib/sports/apisports/football/football.py similarity index 100% rename from lib/sports/football.py rename to lib/sports/apisports/football/football.py diff --git a/lib/sports/apisports/hockey/__init__.py b/lib/sports/apisports/hockey/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/lib/sports/hockey/hockey.py b/lib/sports/apisports/hockey/hockey.py similarity index 88% rename from lib/sports/hockey/hockey.py rename to lib/sports/apisports/hockey/hockey.py index dadfd43..8cb2508 100755 --- a/lib/sports/hockey/hockey.py +++ b/lib/sports/apisports/hockey/hockey.py @@ -1,5 +1,6 @@ from lib.run import Runner from datetime import datetime +from lib.sports.apisports.result import SportApiResult class Hockey(Runner): def __init__(self, token, config, headers): @@ -29,10 +30,11 @@ def url_builder(self, args): urls.update({'next_game': base + f"games?team={self.config.getint('team_id')}&league=57&season=2020&timezone=America/Chicago"}) return urls - async def run(self): + 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(): api_data.update({section: await self.get_data(url, self.headers)}) - return api_data \ No newline at end of file + api_data['sport'] = 'hockey' + return SportApiResult(api_data) \ No newline at end of file diff --git a/lib/sports/apisports/result.py b/lib/sports/apisports/result.py new file mode 100644 index 0000000..cceced4 --- /dev/null +++ b/lib/sports/apisports/result.py @@ -0,0 +1,163 @@ +from lib.sports.sportbase import SportResultBase + +import json + +class SportApiResult(SportResultBase): + def __init__(self, api) -> 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() + else: + self._error = self.set_error() + if 'standings' in self.main_sport: + 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] + if 'next_game' in self.main_sport: + 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})" + + 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 + + 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): + return self._sport + + @property + def get_error(self): + return self._error + + @property + def get_length_position_teams(self): + return len(self.standings) + + @property + def get_standings(self): + return self.standings + + @property + def get_position_teams(self): + return self._positions + + @property + def get_leagues(self): + return self._leagues + + @property + def get_games_played(self): + return self._games_played + + @property + def get_wins(self): + return self._wins + + @property + def get_wins_percentage(self): + return self._wins_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 + + @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) + diff --git a/lib/sports/sportbase.py b/lib/sports/sportbase.py new file mode 100644 index 0000000..d476b20 --- /dev/null +++ b/lib/sports/sportbase.py @@ -0,0 +1,6 @@ +from lib.run import Runner, Caller + +class SportBase(Runner): + pass +class SportResultBase(Caller): + pass \ No newline at end of file diff --git a/lib/sports/sports.py b/lib/sports/sports.py index 26570e0..721ecc9 100755 --- a/lib/sports/sports.py +++ b/lib/sports/sports.py @@ -1,9 +1,6 @@ from typing import List, Tuple, get_args +from lib.sports.apisports.apisports import ApiSports from lib.run import Runner, Caller -from lib.sports.football import Football -from lib.sports.baseball.baseball import Baseball -from lib.sports.basketball.basketball import Basketball -from lib.sports.hockey.hockey import Hockey import os import json import sys @@ -12,195 +9,96 @@ class SportApi(Runner): def __init__(self, config): super().__init__(config) - self.sport = self.config['sport'] - try: - if "sport_token" in self.config['basic']: - self.token = self.config['basic'].get('sport_token') - else: - self.token = os.environ['SPORTTOKEN'] - except KeyError: - self.logger.critical("No Sport Token") - sys.exit("No Sport Token") - self.headers = {'x-apisports-key': 'ebb2c44c416b9a9a0b538e2d73c7dbe6'} def parse_args(self): return super().parse_args() async def run(self): self.logger.info("Running Sports") - sport_data = {"Sport": {}} - if 'football' == self.sport.get('sport').lower(): - self.logger.debug("Running football data") - football = Football(self.token, self.config['sport'], self.headers) - football_return = await football.run() - sport_data['Sport'].update({'football': football_return}) - elif 'baseball' == self.sport.get('sport').lower(): - self.logger.debug('Running baseball data') - baseball = Baseball(self.token, self.config['sport'], self.headers) - baseball_return = await baseball.run() - sport_data['Sport'].update({'baseball': baseball_return}) - elif 'basketball' == self.sport.get('sport').lower(): - self.logger.debug('Got basketball in config') - basketball = Basketball(self.token, self.config['sport'], self.headers) - basketball_return = await basketball.run() - sport_data['Sport'].update({'basketball': basketball_return}) - elif 'hockey' == self.sport.get('sport').lower(): - self.logger.debug('Got Hockey from Config') - hockey = Hockey(self.token, self.config['sport'], self.headers) - hockey_return = await hockey.run() - sport_data['Sport'].update({'hockey': hockey_return}) - return sport_data + # Instead of using a json and dictionary -> Build individual objects + # For Each API and then Bubble up back to sport to be normalized + # Build like a binary Tree + # Can Do checks here to bubble up problems + if self.config['sport']['api'] == "api-sports": + api_sports = ApiSports(self.config) + api_result = await api_sports.run_api_sports() + return SportFinal(api_result) + return -class Sport(Caller): - def __init__(self, api) -> None: - super().__init__() - self.api_type = "" - self.api = api - self.full_sport = self.api['Sport'] - self.sport = [*self.full_sport] - self.main_sport = self.full_sport[self.sport[0]] - if len(self.main_sport['standings']['errors']) != 0: - self._error = self.get_error - else: - if 'standings' in self.main_sport: - 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] - if 'next_game' in self.main_sport: - 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})" - - 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 +class SportFinal(Caller): + """ + Final Normalized Object that goes to + The Sport Matrix. + """ + def __init__(self, api_result) -> None: + # Any Object from any api object + self.api_result = api_result @property + def get_sport(self): + return self.api_result.get_sport + @property def get_error(self): - if isinstance(self.main_sport['standings']['errors'], list): - return True, "" - else: - return False, self.main_sport['standings']['errors']['requests'] - + return self.api_result.get_error @property def get_length_position_teams(self): - return len(self.standings) + return len(self.api_result.standings) @property def get_standings(self): - return self.standings + return self.api_result.standings @property - def get_position_teams(self): - return self._positions + def position_teams(self): + return self.api_result.position_teams @property def get_leagues(self): - return self._leagues + return self.api_result.get_leagues @property def get_games_played(self): - return self._games_played + return self.api_result.games_played @property def get_wins(self): - return self._wins + return self.api_result.get_wins @property def get_wins_percentage(self): - return self._wins_percentage + return self.api_result.win_percentage @property def get_losses(self): - return self._losses + return self.api_result.losses @property def get_loss_percentage(self): - return self._loss_percentage + return self.api_result.loss_percentage @property def get_game_ids(self): - return self._game_ids + return self.api_result.game_ids @property def get_timestamps(self): - return self._timestamps + return self.api_result.timestamps @property def get_teams(self): - return self._teams + return self.api_result.teams @property def get_versus(self): - return self._vs + return self.api_result.vs @property def get_status(self): - return self._status + return self.api_result.status @property def get_scores(self): - return self._game_result + return self.api_result.game_result def get_specific_score(self, game_id): - return self._game_result.get(game_id) - -class APISports(Sport): - pass \ No newline at end of file + return self.api_result.game_result.get(game_id) \ No newline at end of file diff --git a/main.py b/main.py index 6d2cf8d..ec383ba 100755 --- a/main.py +++ b/main.py @@ -17,6 +17,7 @@ from matrix.time import TimeMatrix from matrix.weathermatrix import WeatherMatrix from matrix.sport.sportmatrix import SportMatrix +import traceback import logging @@ -24,7 +25,7 @@ "%(asctime)s:%(module)s: %(message)s" ) sh = logging.StreamHandler() -filehandler = logging.FileHandler("/var/log/ohmyoled.log","a") +filehandler = logging.FileHandler("/var/log/ohmyoled/ohmyoled.log","a") sh.setFormatter(stream_formatter) filehandler.setFormatter(stream_formatter) logger = logging.getLogger(__name__) @@ -115,14 +116,19 @@ async def run_matrix_worker(self, matrix, polled_data): matrix.render(polled_data) async def poll_api_worker(self, matrix): - polled_data = await matrix.poll_api() - return polled_data + try: + polled_data = await matrix.poll_api() + return polled_data + except: + self.logger.error("Error in the Poll_API Worker") + async def main_run(self, loop): try: self.logger.info("Starting OhMyOled") matrix = RGBMatrix(options=self.poll_rgbmatrix()) self.logger.debug("Built Options for RGBMatrix") + # Make the matrixes to a Queue matrixes = await self.init_matrix(matrix) self.logger.info("Starting Matrixes...") first_poll = True @@ -141,6 +147,7 @@ async def main_run(self, loop): logger.info(f"{matrix} rendered for {matrix_finish_time - matrix_start_time:0.4f}s") except Exception as E: logger.error(E) + traceback.print_exc() loop.stop() if __name__ == "__main__": diff --git a/matrix/sport/sportmatrix.py b/matrix/sport/sportmatrix.py index 2cb3634..8dfd46a 100755 --- a/matrix/sport/sportmatrix.py +++ b/matrix/sport/sportmatrix.py @@ -9,18 +9,18 @@ from collections import deque from matrix.error import ErrorMatrix from PIL import ImageFont, Image, ImageDraw -from lib.sports.sports import Sport +from lib.sports.sports import SportFinal from matrix.matrix import Matrix class SportMatrix(Matrix): - def __init__(self, matrix, api: Sport, logger: Logger) -> None: + 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) -> Sport: - return Sport(await self.api.run()) + async def poll_api(self) -> SportFinal: + return SportFinal(await self.api.run()) def baseball_divisions(self, standings: List[Dict]) -> List[str]: american_queue = deque(["American League"]) @@ -183,7 +183,7 @@ async def render(self, api, loop): self.reload_image() if not api.get_error[0]: raise Exception(api.get_error) - if 'baseball' in api.sport: + if 'baseball' in api.get_sport: # Check Data if Offseason if yes Diplay Offseason, Otherwise Display Data # Check data if Game is active, if yes Display game -> Score Inning AT bat Maybe? # Else Display next game @@ -213,7 +213,7 @@ async def render(self, api, loop): await self.render_image() time.sleep(30) - if 'basketball' in api.sport: + if 'basketball' in api.get_sport: # Check Data if Offseason if yes Diplay Offseason, Otherwise Display Data # Check data if Game is active, if yes Display game -> Score Inning AT bat Maybe? # Else Display next game @@ -243,7 +243,7 @@ async def render(self, api, loop): self.render_image() time.sleep(30) - if 'hockey' in api.sport: + if 'hockey' in api.get_sport: self.logger.info("Found Hockey, Displaying Hockey Matrix") if self.check_offseason(api): xpos = 0