|
@@ -2,218 +2,201 @@
|
|
|
|
|
|
import gspread
|
|
|
from oauth2client.service_account import ServiceAccountCredentials
|
|
|
-
|
|
|
-import sys
|
|
|
-import nba_py.player as player
|
|
|
-
|
|
|
import datetime
|
|
|
import time
|
|
|
-
|
|
|
import timeout_decorator
|
|
|
+from nba_api.stats.static import players
|
|
|
+from nba_api.stats.endpoints import playergamelog
|
|
|
|
|
|
-_spreadsheetKey = "12Zv95ZMJ008KXC5ytVnBusjX9wUnpOg0TOLwmxMNOTQ"
|
|
|
+#spreadsheet_key = '12Zv95ZMJ008KXC5ytVnBusjX9wUnpOg0TOLwmxMNOTQ' # 2018 Official
|
|
|
+spreadsheet_key = '14pHOScaGXvN83iCca6_5p6QoViYvo223cIJD9nnl7TI' # 2019 Test
|
|
|
+json_keyfile = 'NBA Playoffs Game-1f9a46f0715c.json'
|
|
|
+#current_day = "" # today, else:
|
|
|
+current_day = datetime.date(2018, 4, 14) # set date manually
|
|
|
+nba_cooldown = 1 # in seconds, don't hammer the NBA.com API too hard
|
|
|
|
|
|
+####################################################################################
|
|
|
|
|
|
-def _getSheets(_key):
|
|
|
+def getWorksheet(spreadsheet_key, json_keyfile):
|
|
|
scope = ['https://spreadsheets.google.com/feeds',
|
|
|
'https://www.googleapis.com/auth/drive']
|
|
|
-
|
|
|
- _credentials = ServiceAccountCredentials.from_json_keyfile_name('NBA Playoffs Game-1f9a46f0715c.json', scope)
|
|
|
-
|
|
|
- _gc = gspread.authorize(_credentials)
|
|
|
-
|
|
|
- # open spreadsheet
|
|
|
- _spreadsheet = _gc.open_by_key(_key)
|
|
|
-
|
|
|
- # open worksheet
|
|
|
- _statsSheet = _spreadsheet.get_worksheet(0)
|
|
|
- _leaderSheet = _spreadsheet.get_worksheet(1)
|
|
|
-
|
|
|
- # count participants
|
|
|
- _numParticipants = _leaderSheet.row_count - 1
|
|
|
-
|
|
|
- return _statsSheet, _leaderSheet
|
|
|
-
|
|
|
-
|
|
|
-def _getNumParticipants(_leaderSheet):
|
|
|
-
|
|
|
- _numParticipants = _leaderSheet.row_count - 1
|
|
|
-
|
|
|
- return _numParticipants
|
|
|
-
|
|
|
-
|
|
|
-def _getRowsFromDate(_statsSheet, _numParticipants):
|
|
|
-
|
|
|
- # get today
|
|
|
- _today = datetime.date.today()
|
|
|
- # else manually set the date
|
|
|
- #_today = datetime.date(2018, 5, 16)
|
|
|
-
|
|
|
- _urlDate = _today.strftime('%m/%d/%Y')
|
|
|
-
|
|
|
- # count rows
|
|
|
- _shtRows = _statsSheet.row_count
|
|
|
-
|
|
|
- # set first date cell
|
|
|
- _startRow = 4
|
|
|
-
|
|
|
- # get date cells
|
|
|
- while _startRow <= _shtRows:
|
|
|
- _dateCell = _statsSheet.cell(_startRow, 1, value_render_option='UNFORMATTED_VALUE')
|
|
|
- _daysSince1900 = _dateCell.value
|
|
|
- _date = datetime.date(1899, 12, 30) + datetime.timedelta(int(_daysSince1900))
|
|
|
-
|
|
|
- if _today == _date:
|
|
|
- break
|
|
|
- elif _today > _date:
|
|
|
- _startRow += _numParticipants
|
|
|
- else:
|
|
|
- _startRow = 0 # no games today
|
|
|
- break
|
|
|
-
|
|
|
- _lastRow = _startRow + _numParticipants - 1
|
|
|
-
|
|
|
- return _startRow, _lastRow, _urlDate
|
|
|
-
|
|
|
-
|
|
|
-def _getPlayersCells(_statsSheet, _startRow, _lastRow):
|
|
|
-
|
|
|
- _playersCells = _statsSheet.range(_startRow, 3, _lastRow, 3)
|
|
|
-
|
|
|
- # filter cells
|
|
|
- for _playerCell in _playersCells[:]:
|
|
|
-
|
|
|
- if _playerCell.value == "":
|
|
|
- _playersCells.remove(_playerCell)
|
|
|
-
|
|
|
- return _playersCells
|
|
|
-
|
|
|
-
|
|
|
-def _getPlayerFirstNameLastName(_player):
|
|
|
- _firstNamelastName = _player.split()
|
|
|
- _firstName = _firstNamelastName[0]
|
|
|
- _firstName = _firstName.replace('.', '')
|
|
|
- _lastName = _firstNamelastName[1]
|
|
|
-
|
|
|
- return _firstName, _lastName
|
|
|
-
|
|
|
-
|
|
|
-@timeout_decorator.timeout(10)
|
|
|
-def _getPlayerID(_firstName, _lastName):
|
|
|
- _playerID = player.get_player(first_name=_firstName,last_name=_lastName)
|
|
|
- return _playerID
|
|
|
-
|
|
|
+ credentials = ServiceAccountCredentials.from_json_keyfile_name(json_keyfile, scope)
|
|
|
+ gc = gspread.authorize(credentials)
|
|
|
+ spreadsheet = gc.open_by_key(spreadsheet_key)
|
|
|
+ worksheet = spreadsheet.get_worksheet(0)
|
|
|
+ return worksheet
|
|
|
+
|
|
|
+def setDates(current_day):
|
|
|
+ if current_day == "":
|
|
|
+ current_day = datetime.datetime.now() - datetime.timedelta(hours=3) # in case games go past midnight
|
|
|
+ current_day = current_day.date()
|
|
|
+ url_date = current_day.strftime('%m/%d/%Y')
|
|
|
+ current_year = current_day.year
|
|
|
+ if current_day.month > 6:
|
|
|
+ current_season = str(current_year) + "-" + str(current_year + 1)[2:]
|
|
|
+ else:
|
|
|
+ current_season = str(current_year - 1) + "-" + str(current_year)[2:]
|
|
|
+ return url_date, current_season, current_day
|
|
|
+
|
|
|
+def getFirstRowLastRow(worksheet, current_day):
|
|
|
+ date_col_values = worksheet.col_values(1) # get all date cell values from column 1
|
|
|
+ final_row = len(date_col_values) # get number of rows in spreadsheet
|
|
|
+ date_cells = worksheet.range('A4:A'+str(final_row)) # get all date cells
|
|
|
+ first_row = ""
|
|
|
+ last_row = ""
|
|
|
+ for date_cell in date_cells:
|
|
|
+ if first_row != "":
|
|
|
+ if date_cell.row == final_row:
|
|
|
+ last_row = final_row
|
|
|
+ elif date_cell.value != "":
|
|
|
+ last_row = date_cell.row - 1
|
|
|
+ break
|
|
|
+ else:
|
|
|
+ continue
|
|
|
+ elif date_cell.value != "":
|
|
|
+ date_cell_day = datetime.datetime.strptime(date_cell.value + " " + str(current_day.year), '%A, %B %d %Y')
|
|
|
+ date_cell_day = date_cell_day.date()
|
|
|
+ if date_cell_day == current_day:
|
|
|
+ first_row = date_cell.row
|
|
|
+ return first_row, last_row
|
|
|
+
|
|
|
+def parsePlayersCells(todays_cells):
|
|
|
+ player_cells = []
|
|
|
+ player_names = []
|
|
|
+ for cell in todays_cells:
|
|
|
+ if cell.col == 3 and cell.value != "":
|
|
|
+ player_cells.append(cell)
|
|
|
+ player_name = cleanPlayerName(cell)
|
|
|
+ player_names.append(player_name)
|
|
|
+ player_names_unique = list(dict.fromkeys(player_names))
|
|
|
+ return player_cells, player_names_unique
|
|
|
+
|
|
|
+def cleanPlayerName(player_cell):
|
|
|
+ raw_name = player_cell.value
|
|
|
+ first_name_last_name = raw_name.split()
|
|
|
+ first_name = first_name_last_name[0]
|
|
|
+ first_name = first_name.replace('.', '')
|
|
|
+ last_name = first_name_last_name[1]
|
|
|
+ player_name = first_name + ' ' + last_name
|
|
|
+ return player_name
|
|
|
+
|
|
|
+def badName(worksheet, player_cell):
|
|
|
+ worksheet.update_cell(player_cell.row, 12, "Fix name, then delete this message!")
|
|
|
|
|
|
@timeout_decorator.timeout(10)
|
|
|
-def _getPlayerStats(_playerID, _urlDate):
|
|
|
-
|
|
|
- _playerStats = {}
|
|
|
- _playerGameLogs = player.PlayerGameLogs(player_id=_playerID,
|
|
|
- season_type='Playoffs',
|
|
|
- date_from=_urlDate,
|
|
|
- date_to=_urlDate)
|
|
|
-
|
|
|
- for _playerGameLog in _playerGameLogs.info():
|
|
|
- for _stat in _playerGameLog.keys():
|
|
|
- _playerStats[_stat] = _playerGameLog[_stat]
|
|
|
-
|
|
|
- return _playerStats
|
|
|
-
|
|
|
-
|
|
|
-def _updatePlayerStatsCells(_statsSheet, _playerCell, _playerStats):
|
|
|
-
|
|
|
- _rowNumber = _playerCell.row
|
|
|
-
|
|
|
- for _stat in _playerStats:
|
|
|
- if _stat == "PTS" and _playerStats[_stat] != "":
|
|
|
- _statsSheet.update_cell(_rowNumber, 4, _playerStats[_stat])
|
|
|
- elif _stat == "REB" and _playerStats[_stat] != "":
|
|
|
- _statsSheet.update_cell(_rowNumber, 5, _playerStats[_stat])
|
|
|
- elif _stat == "AST" and _playerStats[_stat] != "":
|
|
|
- _statsSheet.update_cell(_rowNumber, 6, _playerStats[_stat])
|
|
|
- elif _stat == "STL" and _playerStats[_stat] != "":
|
|
|
- _statsSheet.update_cell(_rowNumber, 7, _playerStats[_stat])
|
|
|
- elif _stat == "BLK" and _playerStats[_stat] != "":
|
|
|
- _statsSheet.update_cell(_rowNumber, 8, _playerStats[_stat])
|
|
|
- elif _stat == "TOV" and _playerStats[_stat] != "":
|
|
|
- _statsSheet.update_cell(_rowNumber, 9, _playerStats[_stat])
|
|
|
-
|
|
|
- if _stat == "WL" and _playerStats[_stat] != "":
|
|
|
- _statsSheet.update_cell(_rowNumber, 11, _playerStats[_stat])
|
|
|
+def getPlayerStats(player_id, url_date, current_season):
|
|
|
+ player_stats = {}
|
|
|
+ player_game_logs = playergamelog.PlayerGameLog(player_id=player_id,
|
|
|
+ season_all=current_season,
|
|
|
+ league_id_nullable='00',
|
|
|
+ season_type_all_star='Playoffs',
|
|
|
+ date_from_nullable=url_date,
|
|
|
+ date_to_nullable=url_date)
|
|
|
+ player_game_logs = player_game_logs.get_dict()
|
|
|
+ player_game_logs_results = player_game_logs.get('resultSets')[0]
|
|
|
+ player_game_logs_headers = player_game_logs_results.get('headers')
|
|
|
+ player_game_logs_values = player_game_logs_results.get('rowSet')[0]
|
|
|
+ player_stats_dict = dict(zip(player_game_logs_headers, player_game_logs_values))
|
|
|
+ return player_stats_dict
|
|
|
+
|
|
|
+@timeout_decorator.timeout(60)
|
|
|
+def buildStatsDict(player_names_unique, url_date, current_season):
|
|
|
+ stats_dict = {}
|
|
|
+ for player_name in player_names_unique:
|
|
|
+ print("Getting " + player_name + "\'s stats from NBA.com")
|
|
|
+ player_info = players.find_players_by_full_name(player_name)
|
|
|
+ player_id = player_info[0].get('id')
|
|
|
+ player_stats_dict = getPlayerStats(player_id, url_date, current_season)
|
|
|
+ stats_dict[player_name] = player_stats_dict
|
|
|
+ time.sleep(nba_cooldown)
|
|
|
+ return stats_dict
|
|
|
+
|
|
|
+def checkStatsDict(player_name, stats_dict):
|
|
|
+ player_stats_dict = stats_dict.get(player_name)
|
|
|
+ return player_stats_dict
|
|
|
+
|
|
|
+def updatePlayerStatsLocal(todays_cells, player_cell, player_stats_dict, update):
|
|
|
+ for cell in todays_cells:
|
|
|
+ if cell.row == player_cell.row:
|
|
|
+ if cell.col == 4 and player_stats_dict.get("PTS") != "" and str(cell.value) != str(player_stats_dict.get("PTS")):
|
|
|
+ cell.value = int(player_stats_dict.get("PTS"))
|
|
|
+ update = True
|
|
|
+ elif cell.col == 5 and player_stats_dict.get("REB") != "" and str(cell.value) != str(player_stats_dict.get("REB")):
|
|
|
+ cell.value = int(player_stats_dict.get("REB"))
|
|
|
+ update = True
|
|
|
+ elif cell.col == 6 and player_stats_dict.get("AST") != "" and str(cell.value) != str(player_stats_dict.get("AST")):
|
|
|
+ cell.value = int(player_stats_dict.get("AST"))
|
|
|
+ update = True
|
|
|
+ elif cell.col == 7 and player_stats_dict.get("STL") != "" and str(cell.value) != str(player_stats_dict.get("STL")):
|
|
|
+ cell.value = int(player_stats_dict.get("STL"))
|
|
|
+ update = True
|
|
|
+ elif cell.col == 8 and player_stats_dict.get("BLK") != "" and str(cell.value) != str(player_stats_dict.get("BLK")):
|
|
|
+ cell.value = int(player_stats_dict.get("BLK"))
|
|
|
+ update = True
|
|
|
+ elif cell.col == 9 and player_stats_dict.get("TOV") != "" and str(cell.value) != str(player_stats_dict.get("TOV")):
|
|
|
+ cell.value = int(player_stats_dict.get("TOV"))
|
|
|
+ update = True
|
|
|
+ elif cell.col == 10 and player_stats_dict.get("WL") != "" and str(cell.value) != str(player_stats_dict.get("WL")):
|
|
|
+ cell.value = str(player_stats_dict.get("WL"))
|
|
|
+ update = True
|
|
|
+ return todays_cells, update
|
|
|
+
|
|
|
|
|
|
|
|
|
-def _badName(_statsSheet, _playerCell):
|
|
|
- _rowNumber = _playerCell.row
|
|
|
- _statsSheet.update_cell(_rowNumber, 12, "Fix name, then delete this message!")
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
|
-###### EXECUTE ########
|
|
|
+ while True:
|
|
|
|
|
|
-while True:
|
|
|
+ url_date, current_season, current_day = setDates(current_day)
|
|
|
+
|
|
|
+ try:
|
|
|
+ worksheet = getWorksheet(spreadsheet_key, json_keyfile)
|
|
|
+ first_row, last_row = getFirstRowLastRow(worksheet, current_day)
|
|
|
+ if first_row == "":
|
|
|
+ print("No games today! Pausing for 1000 seconds...")
|
|
|
+ time.sleep(1000)
|
|
|
+ continue
|
|
|
+ todays_cells = worksheet.range(first_row, 3, last_row, 10)
|
|
|
+ print("Getting a list of today's players...")
|
|
|
+ player_cells, player_names_unique = parsePlayersCells(todays_cells)
|
|
|
+ print(player_names_unique)
|
|
|
+ except gspread.exceptions.APIError:
|
|
|
+ print("API overloaded, retrying in 10 seconds...")
|
|
|
+ time.sleep(10)
|
|
|
+ continue
|
|
|
+ except:
|
|
|
+ time.sleep(10)
|
|
|
+ continue
|
|
|
|
|
|
- try:
|
|
|
- _statsSheet, _leaderSheet = _getSheets(_spreadsheetKey)
|
|
|
- _numParticipants = _getNumParticipants(_leaderSheet)
|
|
|
- _startRow, _lastRow, _urlDate = _getRowsFromDate(_statsSheet, _numParticipants)
|
|
|
- if _startRow == 0:
|
|
|
- print("No games today! Pausing for 1000s")
|
|
|
- time.sleep(1000)
|
|
|
+ try:
|
|
|
+ stats_dict = buildStatsDict(player_names_unique, url_date, current_season)
|
|
|
+ except timeout_decorator.TimeoutError:
|
|
|
+ print("NBA.com API not responding, retrying in 10 seconds...")
|
|
|
+ time.sleep(10)
|
|
|
continue
|
|
|
- else:
|
|
|
- _playersCells = _getPlayersCells(_statsSheet, _startRow, _lastRow)
|
|
|
- except gspread.exceptions.APIError:
|
|
|
- print("API overloaded, adding a 100 second delay...")
|
|
|
- time.sleep(100)
|
|
|
- continue
|
|
|
- except oauth2client.client.HttpAccessTokenRefreshError:
|
|
|
- time.sleep(100)
|
|
|
- continue
|
|
|
-
|
|
|
- for _playerCell in _playersCells:
|
|
|
-
|
|
|
- while True:
|
|
|
+
|
|
|
+ update = False # only update the sheet if it's changed
|
|
|
|
|
|
- try:
|
|
|
- _playerName = _playerCell.value
|
|
|
- print(_playerName)
|
|
|
- except gspread.exceptions.APIError:
|
|
|
- print("API overloaded, adding a 100 second delay...")
|
|
|
- time.sleep(100)
|
|
|
- continue
|
|
|
-
|
|
|
- try:
|
|
|
- _firstName, _lastName = _getPlayerFirstNameLastName(_playerName)
|
|
|
- except:
|
|
|
- #_badName(_statsSheet, _playerCell)
|
|
|
- break
|
|
|
-
|
|
|
- try:
|
|
|
- print("Getting player ID...")
|
|
|
- _playerID = _getPlayerID(_firstName, _lastName)
|
|
|
- except timeout_decorator.TimeoutError:
|
|
|
- print("NBA.com API not responding, retrying...")
|
|
|
- continue
|
|
|
- except:
|
|
|
- #_badName(_statsSheet, _playerCell)
|
|
|
- break
|
|
|
+ for player_cell in player_cells:
|
|
|
+ player_name = cleanPlayerName(player_cell)
|
|
|
+ player_stats_dict = checkStatsDict(player_name, stats_dict)
|
|
|
+ print("Updating " + player_name + "\'s stats locally")
|
|
|
+ todays_cells, update = updatePlayerStatsLocal(todays_cells, player_cell, player_stats_dict, update)
|
|
|
|
|
|
- try:
|
|
|
- print("Getting player stats...")
|
|
|
- _playerStats = _getPlayerStats(_playerID, _urlDate)
|
|
|
- except timeout_decorator.TimeoutError:
|
|
|
- print("NBA.com API not responding, retrying...")
|
|
|
- continue
|
|
|
+ if update == True:
|
|
|
+ while True:
|
|
|
+ try:
|
|
|
+ print("Updating google sheet remotely...")
|
|
|
+ worksheet.update_cells(todays_cells)
|
|
|
+ break
|
|
|
+ except gspread.exceptions.APIError:
|
|
|
+ print("API overloaded, adding a 10 second delay...")
|
|
|
+ time.sleep(10)
|
|
|
+ continue
|
|
|
+ else:
|
|
|
+ print("No remote update necessary...")
|
|
|
+ time.sleep(30)
|
|
|
|
|
|
- try:
|
|
|
- print("Updating stats...")
|
|
|
- _updatePlayerStatsCells(_statsSheet, _playerCell, _playerStats)
|
|
|
- except gspread.exceptions.APIError:
|
|
|
- print("API overloaded, adding a 100 second delay...")
|
|
|
- time.sleep(100)
|
|
|
- continue
|
|
|
-
|
|
|
- break # move to next player in player list
|
|
|
-
|
|
|
- print("Cooling down, safe to break!")
|
|
|
- time.sleep(100)
|
|
|
- continue # restart entire loop
|
|
|
+ continue # restart entire loop
|
|
|
+
|
|
|
|