diff --git a/rewrite/config/__init__.py b/rewrite/config/__init__.py index af716d76..10fb46c8 100644 --- a/rewrite/config/__init__.py +++ b/rewrite/config/__init__.py @@ -1,4 +1,4 @@ -import logging +import logging, platform from datetime import datetime, timedelta @@ -11,8 +11,12 @@ class Config: REFERENCE_FILENAME = "config.json.example" + DATE_FORMAT = "%Y-%m-%d" + TIME_FORMAT_24H = "%H" + TIME_FORMAT_12H = "%#I" if platform.system() == "Windows" else "%-I" + def __init__(self, config_filename, width, height): self.width = width self.height = height @@ -27,6 +31,7 @@ def __init__(self, config_filename, width, height): self.colors = Colors() self.__set_end_of_day() + self.__set_time_format() def today(self): if self.demo_date: @@ -96,7 +101,20 @@ def __set_end_of_day(self): hours, minutes = [int(part) for part in self.end_of_day.split(":")] except: hours, minutes = 0, 0 - ScoreboardLogger.warning(f"Expected 24 hour time for end_of_day in format '12:34'. Received {self.end_of_day}. Defaulting to '00:00'.") + ScoreboardLogger.warning( + f"Expected 24 hour time for end_of_day in format '12:34'. Received {self.end_of_day}. Defaulting to '00:00'." + ) self.eod_hours = hours self.eod_minutes = minutes + + def __set_time_format(self): + if self.time_format == "24h": + self.time_format = Config.TIME_FORMAT_24H + elif self.time_format == "12h": + self.time_format = Config.TIME_FORMAT_12H + else: + ScoreboardLogger.warning( + f"Expected to be one of ('12h', '24h'). Received {self.time_format}. Defaulting to '12h'." + ) + self.time_format = Config.TIME_FORMAT_24H diff --git a/rewrite/data/game.py b/rewrite/data/game.py index 77bb8d30..da1c7c8c 100644 --- a/rewrite/data/game.py +++ b/rewrite/data/game.py @@ -92,6 +92,10 @@ def is_pregame(self): """Returns whether the game is in a pregame state""" return self.status in GameState.GAME_STATE_PREGAME + def is_warmup(self): + """Returns whether the game is in a warmup state""" + return self.status == GameState.WARMUP + def is_complete(self): """Returns whether the game has been completed""" return self.status in GameState.GAME_STATE_COMPLETE @@ -134,6 +138,9 @@ def losing_team(self): return opposite.get(self.winning_team(), None) + def probable_pitcher_id(self, team): + return value_at_keypath(self.data, f"gameData.probablePitchers.{team}").get("id", None) + def decision_pitcher_id(self, decision): return value_at_keypath(self.data, f"liveData.decisions.{decision}").get("id", None) diff --git a/rewrite/presenters/postgame.py b/rewrite/presenters/postgame.py index f978816e..71133d98 100644 --- a/rewrite/presenters/postgame.py +++ b/rewrite/presenters/postgame.py @@ -1,7 +1,10 @@ class PostgamePresenter: PITCHER_UNKNOWN = "Unknown" - def __init__(self, game): + def __init__(self, game, config): + self.game = game + self.config = config + winner_side = game.winning_team() # Defaults diff --git a/rewrite/presenters/pregame.py b/rewrite/presenters/pregame.py new file mode 100644 index 00000000..11b4036e --- /dev/null +++ b/rewrite/presenters/pregame.py @@ -0,0 +1,83 @@ +import tzlocal + + +class PregamePresenter: + TBD = "TBD" + + def __init__(self, game, config): + self.game = game + self.config = config + + self.home_team = game.home_abbreviation() + self.away_team = game.away_abbreviation() + + # TODO: No weather object yet. + # self.pregame_weather = game.pregame_weather() + + self.status = game.status + + away_id = game.probable_pitcher_id("away") + if away_id is not None: + name = game.full_name(away_id) + wins = game.pitcher_stat(away_id, "wins", "away") + losses = game.pitcher_stat(away_id, "losses", "away") + era = game.pitcher_stat(away_id, "era", "away") + self.away_starter = "{} ({}-{} {} ERA)".format(name, wins, losses, era) + else: + self.away_starter = PregamePresenter.TBD + + home_id = game.probable_pitcher_id("home") + if home_id is not None: + name = game.full_name(home_id) + wins = game.pitcher_stat(home_id, "wins", "home") + losses = game.pitcher_stat(home_id, "losses", "home") + era = game.pitcher_stat(home_id, "era", "home") + self.home_starter = "{} ({}-{} {} ERA)".format(name, wins, losses, era) + else: + self.home_starter = PregamePresenter.TBD + + # TODO: No broadcasts yet + # self.national_broadcasts = game.broadcasts() + # self.series_status = game.series_status() + + @property + def start_time(self): + """Converts MLB's pregame times (UTC) into the local time zone""" + try: + time_str = "{}:%M".format(self.config.time_format) + if self.config.time_format == self.config.TIME_FORMAT_12H: + time_str += "%p" + + return self.game.datetime().astimezone(tzlocal.get_localzone()).strftime(time_str) + except: + return PregamePresenter.TBD + + def pregame_info(self): + text = self.away_starter + " vs " + self.home_starter + + # TODO: Add weather, broadcasts, playoffs + # if pregame.national_broadcasts: + # pitchers_text += " TV: " + ", ".join(pregame.national_broadcasts) + # if pregame_weather and pregame.pregame_weather: + # pitchers_text += " Weather: " + pregame.pregame_weather + + # if is_playoffs: + # pitchers_text += " " + pregame.series_status + + return text + + def __str__(self): + s = "<{} {}> {} @ {}; {}; {} vs {}; Forecast: {}; TV: {}".format( + self.__class__.__name__, + hex(id(self)), + self.away_team, + self.home_team, + self.start_time, + self.away_starter, + self.home_starter + # TODO: re-add broadcasts + # , + # self.pregame_weather, + # self.national_broadcasts, + ) + return s diff --git a/rewrite/screens/components/team.py b/rewrite/screens/components/team.py index b99b5590..6e397f13 100644 --- a/rewrite/screens/components/team.py +++ b/rewrite/screens/components/team.py @@ -28,7 +28,9 @@ def render(self): self.__render_background() self.__render_accents() self.__render_team_name() - self.__render_scoreline() + + if not self.game.is_pregame(): + self.__render_scoreline() def __render_background(self): coords = self.layout.coords(f"teams.background.{self.kind}") diff --git a/rewrite/screens/games/postgame.py b/rewrite/screens/games/postgame.py index e088ef8c..7b122999 100644 --- a/rewrite/screens/games/postgame.py +++ b/rewrite/screens/games/postgame.py @@ -11,7 +11,7 @@ class PostgameScreen(GameScreen): MAX_DURATION_SECONDS = 5 def render(self): - presenter = self.create_cached_object("postgame_presenter", PostgamePresenter, self.game) + presenter = self.create_cached_object("postgame_presenter", PostgamePresenter, self.game, self.config) self.__render_final_inning(presenter) self.__render_decision_scroll(presenter) @@ -31,7 +31,7 @@ def __render_final_inning(self, presenter): # text += " " + str(scoreboard.inning.number) center_text = self.create_cached_object( - "postgame_center_text", CenteredText, self.canvas, coords.x, coords.y, font, font_size, color, text + "postgame_final", CenteredText, self.canvas, coords.x, coords.y, font, font_size, color, text ) center_text.render_text() diff --git a/rewrite/screens/games/pregame.py b/rewrite/screens/games/pregame.py index fcf1a108..d6de97c6 100644 --- a/rewrite/screens/games/pregame.py +++ b/rewrite/screens/games/pregame.py @@ -2,19 +2,75 @@ from screens.games.base import GameScreen +from presenters.pregame import PregamePresenter + +from utils.text import ScrollingText, CenteredText + class PregameScreen(GameScreen): MAX_DURATION_SECONDS = 5 def render(self): - self.__render_start_time() + presenter = self.create_cached_object("pregame_presenter", PregamePresenter, self.game, self.config) + + if self.game.is_warmup(): + self.__render_warmup(presenter) + else: + self.__render_start_time(presenter) - def __render_start_time(self): - time_text = str(self.game.datetime()) + self.__render_info(presenter) + + # Overlay banners + self.away_team_banner.render() + self.home_team_banner.render() + + def __render_start_time(self, presenter): + text = presenter.start_time + color = self.colors.graphics_color("pregame.start_time") coords = self.layout.coords("pregame.start_time") font, font_size = self.layout.font_for("pregame.start_time") - # color = colors.graphics_color("pregame.start_time") - # time_x = center_text_position(time_text, coords["x"], font["size"]["width"]) - color = (255, 255, 255) - graphics.DrawText(self.canvas, font, 0, coords.y, color, time_text) + center_text = self.create_cached_object( + "pregame_start_time", CenteredText, self.canvas, coords.x, coords.y, font, font_size, color, text + ) + center_text.render_text() + + def __render_warmup(self, presenter): + text = presenter.status + color = self.colors.graphics_color("pregame.warmup_text") + coords = self.layout.coords("pregame.warmup_text") + font, font_size = self.layout.font_for("pregame.warmup_text") + + center_text = self.create_cached_object( + "pregame_start_time", CenteredText, self.canvas, coords.x, coords.y, font, font_size, color, text + ) + center_text.render_text() + + def __render_info(self, presenter): + text = presenter.pregame_info() + + coords = self.layout.coords("pregame.scrolling_text") + font, font_size = self.layout.font_for("pregame.scrolling_text") + + color = self.colors.graphics_color("pregame.scrolling_text") + bgcolor = self.colors.graphics_color("default.background") + + scroller = self.create_cached_object( + "pregame_scroller", + ScrollingText, + self.canvas, + coords.x, + coords.y, + coords.width, + coords.width, + font, + font_size, + color, + bgcolor, + text, + ) + scroller.render_text() + + # TODO: This can be better, but for now this screen is done when the text is gone. + if scroller.finished: + self.data.schedule.request_next_game() diff --git a/rewrite/utils/__init__.py b/rewrite/utils/__init__.py index d8a67359..e0ddcaa5 100644 --- a/rewrite/utils/__init__.py +++ b/rewrite/utils/__init__.py @@ -1,4 +1,4 @@ -import argparse, os, json +import argparse, json, os from utils import logger as ScoreboardLogger