ripred

sports.py

May 24th, 2022 (edited)
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.88 KB | None | 0 0
  1. #!/usr/bin/env python3 -O
  2. # sports.py
  3.  
  4. """
  5. A python script to retrieve and print
  6. the day's hockey and/or baseball games
  7.  
  8. Works well with GeekTool or Conky.
  9. For today's hockey games use:
  10.  
  11.  "python3 sports.py H C"
  12.  
  13. And for today's baseball games.
  14.  
  15.  "python3 sports.py B C"
  16. """
  17.  
  18. #
  19. # originally written 2016 ++tmw
  20. #
  21.  
  22. import sys
  23. import ssl
  24. import urllib
  25. import urllib.request
  26. import json
  27. from datetime import datetime
  28. import time
  29.  
  30. # Set up lists of favorite teams and rival teams for both hockey and baseball
  31. FAVORITES = [
  32.     'Dallas Stars',
  33.     'Pittsburgh Penguins',
  34. #   'Detroit Red Wings',
  35. #   'Calgary Flames',
  36. #   "Vancouver Canucks",
  37.  
  38.     'Texas Rangers',
  39. #   'Arizona Diamondbacks',
  40. #   'Houston Astros'
  41. ]
  42.  
  43. RIVALS = [
  44. #   'Winnipeg Jets',
  45. #   'Nashville Predators',
  46. #   'Arizona Coyotes',
  47. #   'Minnesota Wild',
  48. #   'Colorado Avalanche',
  49. #   'St Louis Blues',
  50. #   'Chicago Blackhawks',
  51. #   'Los Angeles Angels',
  52.  
  53. #   'Houston Astros',
  54. #   'Seattle Mariners',
  55. #   'Oakland Athletics'
  56. ]
  57.  
  58. # ANSI color constant escape sequences:
  59. ANSI_PRE = '\x1b['
  60. ANSI_POST = 'm'
  61. ANSI_RESET = ANSI_PRE + '0' + ANSI_POST
  62. BLACK = 30
  63. RED = 31
  64. GREEN = 32
  65. YELLOW = 33
  66. BLUE = 34
  67. PURPLE = 35
  68. CYAN = 36
  69. WHITE = 37
  70.  
  71. # Option flags set by user at cli
  72.  
  73. # use ANSI color sequences
  74. USE_COLOR = False
  75.  
  76. # add color to the game's timestamp
  77. COLOR_TIME = False
  78.  
  79. # use a black background
  80. BLACK_BACKGROUND = False
  81.  
  82. # use bright (or bold) colors
  83. USE_BRIGHT = False
  84.  
  85.  
  86. def color(clr, text):
  87.     """
  88.        function to wrap ANSI escape sequences around text
  89.        to set the display color of the text
  90.  
  91.        :param clr: one of the color constants above
  92.        :param text: the text string to color
  93.        :return: the text with the proper ANSI prefix and suffix bytes
  94.    """
  95.     if not USE_COLOR:
  96.         return text
  97.     background_color = ''
  98.     if BLACK_BACKGROUND:
  99.         background_color = ';40'
  100.     prefix = '0;'
  101.     if USE_BRIGHT:
  102.         prefix = '1;'
  103.  
  104.     return ''.join([ANSI_PRE, prefix, str(clr), background_color, ANSI_POST, text, ANSI_RESET])
  105.  
  106.  
  107. def test_ansi_colors():
  108.     """
  109.    print out all combinations of ANSI color foregrounds and backgrounds
  110.    """
  111.     for foreground in range(30, 38):
  112.         output = ''
  113.         str_f = str(foreground)
  114.         for background in range(40, 48):
  115.             str_b = str(background)
  116.             output += ''.join([ANSI_PRE, str_f, ';', str_b, ANSI_POST, str_f, '/', str_b])
  117.         print(output + ANSI_RESET)
  118.  
  119.  
  120. def datetime_from_utc_to_local(utc_datetime):
  121.     """
  122.    convert a datetime from UTC to local time
  123.    :param utc_datetime: datetime object of the game start time in UTC
  124.    :return: returns the datetime converted from UTC to local time
  125.    """
  126.     now_timestamp = time.time()
  127.     offset = datetime.fromtimestamp(now_timestamp) - datetime.utcfromtimestamp(now_timestamp)
  128.     return utc_datetime + offset
  129.  
  130.  
  131. def get_game_time(game):
  132.     """
  133.    get the game time in local time
  134.    :param game: dictionary of a specific game
  135.    :return: returns the game start time converted from UTC to local time
  136.    """
  137.     game_time_str = game['gameDate']  # 2018-03-03T18:00:00Z
  138.     game_time = datetime.strptime(game_time_str, '%Y-%m-%dT%H:%M:%SZ')
  139.  
  140.     return datetime_from_utc_to_local(game_time)
  141.  
  142.  
  143. def get_teams(game):
  144.     """
  145.    retrieve the two teams in this game and the notations indicating
  146.    if a favorite or rival team is involved
  147.    :param game: dictionary of a specific game
  148.    :return: returns a tuple containing team1, team2, and the game notations
  149.    """
  150.     team1 = game['teams']['away']['team']['name']
  151.     team2 = game['teams']['home']['team']['name']
  152.     note1 = ' '
  153.     note2 = ' '
  154.  
  155.     # if team1.find('Canadiens') >= 0:
  156.     #     team1 = 'Montreal Canadiens'
  157.     # if team2.find('Canadiens') >= 0:
  158.     #     team2 = 'Montreal Canadiens'
  159.  
  160.     if team1 in FAVORITES or team2 in FAVORITES:
  161.         note1 = '*'
  162.     if team1 in RIVALS or team2 in RIVALS:
  163.         note2 = '!'
  164.  
  165.     # Add optional ANSI color sequences to favorite and rival team names
  166.     if team1 in FAVORITES:
  167.         team1 = color(GREEN, team1)
  168.     if team2 in FAVORITES:
  169.         team2 = color(GREEN, team2)
  170.     if team1 in RIVALS:
  171.         team1 = color(RED, team1)
  172.     if team2 in RIVALS:
  173.         team2 = color(RED, team2)
  174.  
  175.     return team1, team2, note1 + note2
  176.  
  177.  
  178. def create_games_dict(all_games):
  179.     """
  180.    create a dictionary of today's games with formatted descriptions
  181.    :param all_games: dictionary of today's games retrieved from the web api
  182.    :return: returns a dictionary of today's games with formatted descriptions
  183.    """
  184.     game_dictionary = {}
  185.     at_str = color(WHITE, ' at ')
  186.     for game in all_games:
  187.         # get local time for game
  188.         game_time = get_game_time(game)
  189.  
  190.         # Get lowercase version of 'AM/PM'
  191.         am_pm_str = game_time.strftime('%p').lower()
  192.  
  193.         # Create game time string
  194.         game_time_str = game_time.strftime('%I:%M ' + am_pm_str)
  195.         if COLOR_TIME:
  196.             game_time_str = color(BLUE, game_time_str)
  197.  
  198.         # Get the teams involved
  199.         (team1, team2, notations) = get_teams(game)
  200.  
  201.         # Create the game description string
  202.         game_desc_str = ''.join([color(WHITE, notations + '   '),
  203.                                  game_time_str, '  -  ', team1,
  204.                                  at_str, team2])
  205.  
  206.         # Add the game to the games dictionary indexed by game start time
  207.         gametime_key = game_time.strftime('%H:%M') + team1
  208.         game_dictionary[gametime_key] = game_desc_str
  209.  
  210.     return game_dictionary
  211.  
  212.  
  213. def create_test_data_file(filename, json_data):
  214.     """
  215.    create a file containing test data to use as input during unit tests
  216.    :param filename: the file name to write the data to
  217.    :param json_data: the json data to write to the test input file
  218.    """
  219.     json_str = str(json_data).replace("'", '"')
  220.     json_str = json_str.replace('False', '"False"')
  221.     json_str = json_str.replace('True', '"True"')
  222.     if json_str[0] == "'" and json_str[-1] == "'":
  223.         json_str = json_str[1:-1]
  224.     with open(filename, 'wt') as filehandle:
  225.         filehandle.write(json_str)
  226.  
  227.  
  228. def get_json_data(api_url):
  229.     """
  230.    retrieve the json data returned from the specified REST url
  231.    :param api_url: the url to retrieve the data from
  232.    :return: returns the json data as a string
  233.    """
  234.     ssl_context = ssl.create_default_context()
  235.     ssl_context.check_hostname = False
  236.     ssl_context.verify_mode = ssl.CERT_NONE
  237.     # ssl._create_default_https_context = ssl._create_unverified_context
  238.     with urllib.request.urlopen(api_url, context=ssl_context) as url:
  239.         http_info = url.info()
  240.         raw_data = url.read().decode(http_info.get_content_charset())
  241.         json_data = json.loads(raw_data)
  242.         return json_data
  243. #   create_test_data_file('test.json', json_data)
  244.     return {}
  245.  
  246.  
  247. def get_games_count(json_data):
  248.     """
  249.    get the number of games contained in the specified json data
  250.    :param json_data: a string containing the json data retrieved from a previous web REST api call
  251.    :return: returns the total number of games (0 if there are none)
  252.    """
  253.     total_games = 0
  254.     if 'totalGames' in json_data:
  255.         total_games = int(json_data['totalGames'])
  256.     return total_games
  257.  
  258.  
  259. def get_todays_games(json_data):
  260.     """
  261.    create a dictionary of game description string
  262.    values with start time keys. Also create a list
  263.    of those keys sorted in ascending start time order.
  264.  
  265.    :param json_data: dictionary of games data from the web
  266.    :return: returns a tuple of the game_dict, display_keys
  267.    """
  268.     # Sort the games based on start time
  269.     if not 'dates' in json_data or not json_data['dates']:
  270.         return {}, {}
  271.     all_games = json_data['dates'][0]['games']
  272.     game_dictionary = create_games_dict(all_games)
  273.     sorted_games = sorted(game_dictionary.keys())
  274.     return game_dictionary, sorted_games
  275.  
  276.  
  277. def output_todays_games(games_dict, key_list, sport_name):
  278.     """
  279.    output today's games in ascending order by their start times
  280.  
  281.    :param games_dict: the dictionary of game times to game descriptions
  282.    :param key_list: list of gametime keys sorted in ascending order
  283.    :param sport_name: game type string used in case the are no games
  284.    :return: returns nothing
  285.    """
  286.     if not games_dict or not key_list:
  287.         output = ''
  288.         if sport_name == 'hockey':
  289.             output = 'Sorry.  '  # 🏒 speak proper Canadian 😎
  290.         output += ''.join(['There are no ', sport_name, ' games today'])
  291.         print(color(WHITE, '     ' + output))
  292.         return
  293.     for key in key_list:
  294.         print(games_dict[key])
  295.  
  296.  
  297. # parse and process the command line
  298. def process_command_line(argv):
  299.     """
  300.    process the command line options arguments
  301.    :param argv: list of arguments passed on the command line
  302.    """
  303.     global USE_COLOR, BLACK_BACKGROUND, COLOR_TIME, USE_BRIGHT
  304.     nhl_api_url = 'https://statsapi.web.nhl.com/api/v1/schedule'
  305.     mlb_api_url = 'https://statsapi.mlb.com/api/v1/schedule?sportId=1'
  306.     json_data = {}
  307.     sport_name = ''
  308.  
  309.     for cmd in argv[1:]:
  310.         cmd = cmd.upper()
  311.         USE_COLOR = True if cmd == 'C' else USE_COLOR
  312.         USE_BRIGHT = True if cmd == 'I' else USE_BRIGHT
  313.         BLACK_BACKGROUND = True if cmd == 'K' else BLACK_BACKGROUND
  314.         COLOR_TIME = True if cmd == 'T' else COLOR_TIME
  315.         USE_COLOR = False if cmd == 'N' else USE_COLOR
  316.  
  317.         if cmd == 'H':
  318.             json_data = get_json_data(nhl_api_url)
  319.             sport_name = 'hockey'
  320.         if cmd == 'B':
  321.             json_data = get_json_data(mlb_api_url)
  322.             sport_name = 'baseball'
  323.  
  324.     if json_data:
  325.         games_dict, keys = get_todays_games(json_data)
  326.         output_todays_games(games_dict, keys, sport_name)
  327.  
  328.  
  329. if __name__ == "__main__":
  330.     process_command_line(sys.argv)
  331.  
Add Comment
Please, Sign In to add comment