From e2b5ae3b555e4b2c84e770d4667ca12cbf3f8132 Mon Sep 17 00:00:00 2001 From: Davis Date: Mon, 24 Aug 2026 19:49:15 +0200 Subject: [PATCH] reformatted entire repo using black --- espn_api/_version.py | 2 +- espn_api/base_league.py | 171 ++-- espn_api/base_offer.py | 91 +- espn_api/base_pick.py | 36 +- espn_api/base_settings.py | 79 +- espn_api/baseball/__init__.py | 17 +- espn_api/baseball/activity.py | 34 +- espn_api/baseball/box_player.py | 43 +- espn_api/baseball/box_score.py | 166 ++-- espn_api/baseball/constant.py | 450 +++++---- espn_api/baseball/league.py | 221 +++-- espn_api/baseball/matchup.py | 48 +- espn_api/baseball/player.py | 151 +-- espn_api/baseball/settings.py | 2 +- espn_api/baseball/team.py | 95 +- espn_api/baseball/transaction.py | 59 +- espn_api/baseball/utils.py | 5 +- espn_api/basketball/__init__.py | 12 +- espn_api/basketball/activity.py | 49 +- espn_api/basketball/box_player.py | 47 +- espn_api/basketball/box_score.py | 160 ++-- espn_api/basketball/constant.py | 290 +++--- espn_api/basketball/league.py | 230 +++-- espn_api/basketball/matchup.py | 57 +- espn_api/basketball/player.py | 118 ++- espn_api/basketball/team.py | 89 +- espn_api/basketball/transaction.py | 25 +- espn_api/football/__init__.py | 9 +- espn_api/football/activity.py | 54 +- espn_api/football/box_player.py | 77 +- espn_api/football/box_score.py | 112 ++- espn_api/football/constant.py | 952 +++++++++---------- espn_api/football/league.py | 287 ++++-- espn_api/football/matchup.py | 25 +- espn_api/football/player.py | 123 ++- espn_api/football/settings.py | 25 +- espn_api/football/team.py | 125 +-- espn_api/football/transaction.py | 29 +- espn_api/football/utils.py | 21 +- espn_api/hockey/__init__.py | 9 +- espn_api/hockey/activity.py | 25 +- espn_api/hockey/box_player.py | 35 +- espn_api/hockey/box_score.py | 50 +- espn_api/hockey/constant.py | 230 ++--- espn_api/hockey/league.py | 172 ++-- espn_api/hockey/matchup.py | 60 +- espn_api/hockey/player.py | 62 +- espn_api/hockey/record.py | 30 +- espn_api/hockey/team.py | 63 +- espn_api/requests/__init__.py | 4 +- espn_api/requests/constant.py | 16 +- espn_api/requests/espn_requests.py | 173 ++-- espn_api/utils/logger.py | 11 +- espn_api/utils/utils.py | 5 +- espn_api/wbasketball/__init__.py | 12 +- espn_api/wbasketball/activity.py | 34 +- espn_api/wbasketball/box_player.py | 43 +- espn_api/wbasketball/box_score.py | 58 +- espn_api/wbasketball/constant.py | 184 ++-- espn_api/wbasketball/league.py | 174 ++-- espn_api/wbasketball/matchup.py | 60 +- espn_api/wbasketball/player.py | 80 +- espn_api/wbasketball/team.py | 71 +- setup.py | 29 +- tests/baseball/integration/test_league.py | 19 +- tests/baseball/unit/test_box_score.py | 76 +- tests/baseball/unit/test_league.py | 377 ++++---- tests/baseball/unit/test_player.py | 388 ++++---- tests/baseball/unit/test_settings.py | 122 +-- tests/baseball/unit/test_team.py | 67 +- tests/baseball/unit/test_transaction.py | 179 ++-- tests/basketball/integration/test_league.py | 47 +- tests/basketball/unit/test_activity.py | 328 +++---- tests/basketball/unit/test_league.py | 236 ++--- tests/basketball/unit/test_player.py | 232 ++--- tests/basketball/unit/test_team.py | 431 +++++---- tests/espn_requests/test_access_denied.py | 52 +- tests/espn_requests/test_espn_requests.py | 13 +- tests/football/integration/test_league.py | 48 +- tests/football/unit/test_league.py | 222 +++-- tests/football/unit/test_past_league.py | 74 +- tests/hockey/integration/test_league.py | 6 +- tests/hockey/unit/test_league.py | 142 +-- tests/hockey/unit/test_player.py | 13 +- tests/hockey/unit/test_team.py | 25 +- tests/wbasketball/integration/test_league.py | 11 +- tests/wbasketball/unit/test_activity.py | 278 +++--- tests/wbasketball/unit/test_box_score.py | 293 +++--- tests/wbasketball/unit/test_player.py | 212 +++-- 89 files changed, 5773 insertions(+), 4394 deletions(-) diff --git a/espn_api/_version.py b/espn_api/_version.py index 50fa61e7c..6f7098722 100644 --- a/espn_api/_version.py +++ b/espn_api/_version.py @@ -1 +1 @@ -__version__ = '0.46.0' +__version__ = "0.46.0" diff --git a/espn_api/base_league.py b/espn_api/base_league.py index c82570999..29ad4a52e 100644 --- a/espn_api/base_league.py +++ b/espn_api/base_league.py @@ -6,10 +6,20 @@ from .utils.logger import Logger from .requests.espn_requests import EspnFantasyRequests + class BaseLeague(ABC): - '''Creates a League instance for Public/Private ESPN league''' - def __init__(self, league_id: int, year: int, sport: str, espn_s2=None, swid=None, debug=False): - self.logger = Logger(name=f'{sport} league', debug=debug) + """Creates a League instance for Public/Private ESPN league""" + + def __init__( + self, + league_id: int, + year: int, + sport: str, + espn_s2=None, + swid=None, + debug=False, + ): + self.logger = Logger(name=f"{sport} league", debug=debug) self.league_id = league_id self.year = year self.teams = [] @@ -19,69 +29,103 @@ def __init__(self, league_id: int, year: int, sport: str, espn_s2=None, swid=Non cookies = None if espn_s2 and swid: - cookies = { - 'espn_s2': espn_s2, - 'SWID': swid - } - self.espn_request = EspnFantasyRequests(sport=sport, year=year, league_id=league_id, cookies=cookies, logger=self.logger) + cookies = {"espn_s2": espn_s2, "SWID": swid} + self.espn_request = EspnFantasyRequests( + sport=sport, + year=year, + league_id=league_id, + cookies=cookies, + logger=self.logger, + ) def __repr__(self): - return 'League(%s, %s)' % (self.league_id, self.year, ) + return "League(%s, %s)" % ( + self.league_id, + self.year, + ) - def _fetch_league(self, SettingsClass = BaseSettings): + def _fetch_league(self, SettingsClass=BaseSettings): data = self.espn_request.get_league() - self.currentMatchupPeriod = data['status']['currentMatchupPeriod'] - self.scoringPeriodId = data['scoringPeriodId'] - self.firstScoringPeriod = data['status']['firstScoringPeriod'] - self.finalScoringPeriod = data['status']['finalScoringPeriod'] + self.currentMatchupPeriod = data["status"]["currentMatchupPeriod"] + self.scoringPeriodId = data["scoringPeriodId"] + self.firstScoringPeriod = data["status"]["firstScoringPeriod"] + self.finalScoringPeriod = data["status"]["finalScoringPeriod"] self.previousSeasons = [ year for year in data["status"]["previousSeasons"] if year < self.year ] if self.year < 2018: - self.current_week = data['scoringPeriodId'] + self.current_week = data["scoringPeriodId"] else: - self.current_week = self.scoringPeriodId if self.scoringPeriodId <= data['status']['finalScoringPeriod'] else data['status']['finalScoringPeriod'] - self.settings = SettingsClass(data['settings']) - self.members = data.get('members', []) + self.current_week = ( + self.scoringPeriodId + if self.scoringPeriodId <= data["status"]["finalScoringPeriod"] + else data["status"]["finalScoringPeriod"] + ) + self.settings = SettingsClass(data["settings"]) + self.members = data.get("members", []) return data def _fetch_draft(self): - '''Creates list of Pick objects from the leagues draft''' + """Creates list of Pick objects from the leagues draft""" data = self.espn_request.get_league_draft() # League has not drafted yet - if not data.get('draftDetail', {}).get('drafted'): + if not data.get("draftDetail", {}).get("drafted"): return - picks = data.get('draftDetail', {}).get('picks', []) + picks = data.get("draftDetail", {}).get("picks", []) for pick in picks: - team = self.get_team_data(pick.get('teamId')) - playerId = pick.get('playerId') - playerName = '' + team = self.get_team_data(pick.get("teamId")) + playerId = pick.get("playerId") + playerName = "" if playerId in self.player_map: playerName = self.player_map[playerId] - round_num = pick.get('roundId') - round_pick = pick.get('roundPickNumber') - bid_amount = pick.get('bidAmount') - keeper_status = pick.get('keeper') - nominatingTeam = self.get_team_data(pick.get('nominatingTeamId')) - self.draft.append(BasePick(team, playerId, playerName, round_num, round_pick, bid_amount, keeper_status, nominatingTeam)) - - def _fetch_teams(self, data, TeamClass, pro_schedule = None): - '''Fetch teams in league''' + round_num = pick.get("roundId") + round_pick = pick.get("roundPickNumber") + bid_amount = pick.get("bidAmount") + keeper_status = pick.get("keeper") + nominatingTeam = self.get_team_data(pick.get("nominatingTeamId")) + self.draft.append( + BasePick( + team, + playerId, + playerName, + round_num, + round_pick, + bid_amount, + keeper_status, + nominatingTeam, + ) + ) + + def _fetch_teams(self, data, TeamClass, pro_schedule=None): + """Fetch teams in league""" self.teams = [] - teams = data['teams'] - schedule = data['schedule'] - seasonId = data['seasonId'] - members = data.get('members', []) + teams = data["teams"] + schedule = data["schedule"] + seasonId = data["seasonId"] + members = data.get("members", []) team_roster = {} - for team in data['teams']: - team_roster[team['id']] = team.get('roster', {}) + for team in data["teams"]: + team_roster[team["id"]] = team.get("roster", {}) for team in teams: - roster = team_roster[team['id']] - owners = [member for member in members if member.get('id') in team.get('owners', [])] - self.teams.append(TeamClass(team, roster=roster, schedule=schedule, year=seasonId, owners=owners, pro_schedule=pro_schedule)) + roster = team_roster[team["id"]] + owners = [ + member + for member in members + if member.get("id") in team.get("owners", []) + ] + self.teams.append( + TeamClass( + team, + roster=roster, + schedule=schedule, + year=seasonId, + owners=owners, + pro_schedule=pro_schedule, + ) + ) # sort by team ID self.teams = sorted(self.teams, key=lambda x: x.team_id, reverse=False) @@ -91,53 +135,64 @@ def _fetch_players(self): # Map all player id's to player name for player in data: # two way map to find playerId's by name - self.player_map[player['id']] = player['fullName'] + self.player_map[player["id"]] = player["fullName"] # if two players have the same fullname use first one for now TODO update for multiple player names - if player['fullName'] not in self.player_map: - self.player_map[player['fullName']] = player['id'] + if player["fullName"] not in self.player_map: + self.player_map[player["fullName"]] = player["id"] def _get_pro_schedule(self, scoringPeriodId: int = None): data = self.espn_request.get_pro_schedule() - pro_teams = data['settings']['proTeams'] + pro_teams = data["settings"]["proTeams"] pro_team_schedule = {} for team in pro_teams: - pro_game = team.get('proGamesByScoringPeriod', {}) - if team['id'] != 0 and (str(scoringPeriodId) in pro_game.keys() and pro_game[str(scoringPeriodId)]): + pro_game = team.get("proGamesByScoringPeriod", {}) + if team["id"] != 0 and ( + str(scoringPeriodId) in pro_game.keys() + and pro_game[str(scoringPeriodId)] + ): game_data = pro_game[str(scoringPeriodId)][0] - pro_team_schedule[team['id']] = (game_data['homeProTeamId'], game_data['date']) if team['id'] == game_data['awayProTeamId'] else (game_data['awayProTeamId'], game_data['date']) + pro_team_schedule[team["id"]] = ( + (game_data["homeProTeamId"], game_data["date"]) + if team["id"] == game_data["awayProTeamId"] + else (game_data["awayProTeamId"], game_data["date"]) + ) return pro_team_schedule - + def _get_all_pro_schedule(self): data = self.espn_request.get_pro_schedule() - pro_teams = data.get('settings', {}).get('proTeams', {}) + pro_teams = data.get("settings", {}).get("proTeams", {}) pro_team_schedule = {} for team in pro_teams: - pro_game = team.get('proGamesByScoringPeriod', {}) - pro_team_schedule[team['id']] = pro_game + pro_game = team.get("proGamesByScoringPeriod", {}) + pro_team_schedule[team["id"]] = pro_game return pro_team_schedule def _get_offers(self, week: int = None): - '''Returns a list of free agent auction bids''' + """Returns a list of free agent auction bids""" if week is None: bids = [] - for week in range(0, self.finalScoringPeriod+1): + for week in range(0, self.finalScoringPeriod + 1): data = self.espn_request.get_league_offers(week=week) - transactions = data.get('transactions', []) + transactions = data.get("transactions", []) if transactions: # Only append non-empty transaction lists for t in transactions: bids.append(t) else: data = self.espn_request.get_league_offers(week=week) - bids = data.get('transactions', []) + bids = data.get("transactions", []) return bids def standings(self) -> List: - standings = sorted(self.teams, key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, reverse=False) + standings = sorted( + self.teams, + key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, + reverse=False, + ) return standings def get_team_data(self, team_id: int) -> List: diff --git a/espn_api/base_offer.py b/espn_api/base_offer.py index acf396a0e..f1eebbbb6 100644 --- a/espn_api/base_offer.py +++ b/espn_api/base_offer.py @@ -3,48 +3,56 @@ class Offer(object): def __init__(self, data, player_map, get_team_data): - status = data['status'] - self.id = data['id'] + status = data["status"] + self.id = data["id"] self.dateTime = None - if status == 'CANCELED': - self.result = 'Canceled' + if status == "CANCELED": + self.result = "Canceled" else: - if status == 'EXECUTED': - self.result = 'Processed' - elif status == 'FAILED_INVALIDPLAYERSOURCE': - self.result = 'Outbid' - elif status == 'FAILED_AUCTIONBUDGETEXCEEDED': - self.result = 'Budget Exceeded' - elif status == 'FAILED_POSITIONLIMIT': - self.result = 'Position Limit Exceeded' - elif status == 'FAILED_ROSTERLOCK': - self.result = 'Failed Due to Roster Lock' - elif status == 'FAILED_PLAYERALREADYDROPPED' or status == 'FAILED_ROSTERLIMIT' or status == 'PENDING': - self.result = 'Player already dropped' + if status == "EXECUTED": + self.result = "Processed" + elif status == "FAILED_INVALIDPLAYERSOURCE": + self.result = "Outbid" + elif status == "FAILED_AUCTIONBUDGETEXCEEDED": + self.result = "Budget Exceeded" + elif status == "FAILED_POSITIONLIMIT": + self.result = "Position Limit Exceeded" + elif status == "FAILED_ROSTERLOCK": + self.result = "Failed Due to Roster Lock" + elif ( + status == "FAILED_PLAYERALREADYDROPPED" + or status == "FAILED_ROSTERLIMIT" + or status == "PENDING" + ): + self.result = "Player already dropped" else: self.result = status # fixes bug with unprocessed waivers stuck on "PENDING" status - if 'processDate' in data: - self.dateTime = datetime.fromtimestamp(int(data['processDate'] / 1000)) # convert from milliseconds to seconds - self.amount = data['bidAmount'] - self.teamId = data['teamId'] + if "processDate" in data: + self.dateTime = datetime.fromtimestamp( + int(data["processDate"] / 1000) + ) # convert from milliseconds to seconds + self.amount = data["bidAmount"] + self.teamId = data["teamId"] self.droppedPlayer = None - for item in data['items']: - if item['type'] == 'ADD': - self.player = item['playerId'] - elif item['type'] == 'DROP' and self.result == 'Processed': - self.droppedPlayer = item['playerId'] + for item in data["items"]: + if item["type"] == "ADD": + self.player = item["playerId"] + elif item["type"] == "DROP" and self.result == "Processed": + self.droppedPlayer = item["playerId"] def __lt__(self, other): # sort by status, then bid amount - result_ranking = {'Processed': 7, - 'Outbid': 6, - 'Player already dropped': 5, - 'Budget Exceeded': 4, - 'Position Limit Exceeded': 3, - 'Failed Due to Roster Lock': 2, - 'CANCELLED': 1, - 'PENDING': 0} + result_ranking = { + "Processed": 7, + "Outbid": 6, + "Player already dropped": 5, + "Budget Exceeded": 4, + "Position Limit Exceeded": 3, + "Failed Due to Roster Lock": 2, + "CANCELLED": 1, + "PENDING": 0, + } if result_ranking[self.result] != result_ranking[other.result]: return result_ranking[self.result] < result_ranking[other.result] else: @@ -52,13 +60,16 @@ def __lt__(self, other): return self.amount < other.amount def __repr__(self): - if self.result == 'Canceled': - return 'Canceled bid' + if self.result == "Canceled": + return "Canceled bid" else: - ret_string = 'Offer(Date:{0}, Player:{1}, Team:{2}, Result:{3}, Bid:{4}'.format(self.dateTime, self.player, self.teamId, - self.result, self.amount) + ret_string = ( + "Offer(Date:{0}, Player:{1}, Team:{2}, Result:{3}, Bid:{4}".format( + self.dateTime, self.player, self.teamId, self.result, self.amount + ) + ) if self.droppedPlayer: - ret_string += ', Dropped:{0})'.format(self.droppedPlayer) + ret_string += ", Dropped:{0})".format(self.droppedPlayer) else: - ret_string += ')' - return ret_string \ No newline at end of file + ret_string += ")" + return ret_string diff --git a/espn_api/base_pick.py b/espn_api/base_pick.py index 11cc04d0c..bebbbab58 100644 --- a/espn_api/base_pick.py +++ b/espn_api/base_pick.py @@ -1,7 +1,17 @@ - class BasePick(object): - ''' Pick represents a pick in draft ''' - def __init__(self, team, playerId, playerName, round_num, round_pick, bid_amount, keeper_status, nominatingTeam): + """Pick represents a pick in draft""" + + def __init__( + self, + team, + playerId, + playerName, + round_num, + round_pick, + bid_amount, + keeper_status, + nominatingTeam, + ): self.team = team self.playerId = playerId self.playerName = playerName @@ -12,7 +22,23 @@ def __init__(self, team, playerId, playerName, round_num, round_pick, bid_amount self.nominatingTeam = nominatingTeam def __repr__(self): - return 'Pick(R:%s P:%s, %s, %s)' % (self.round_num, self.round_pick, self.playerName, self.team) + return "Pick(R:%s P:%s, %s, %s)" % ( + self.round_num, + self.round_pick, + self.playerName, + self.team, + ) def auction_repr(self): - return ', '.join(map(str, [self.team, self.playerId, self.playerName, self.bid_amount, self.keeper_status])) \ No newline at end of file + return ", ".join( + map( + str, + [ + self.team, + self.playerId, + self.playerName, + self.bid_amount, + self.keeper_status, + ], + ) + ) diff --git a/espn_api/base_settings.py b/espn_api/base_settings.py index e7478fef3..13d2543c0 100644 --- a/espn_api/base_settings.py +++ b/espn_api/base_settings.py @@ -1,36 +1,55 @@ class BaseSettings(object): - '''Creates Settings object''' + """Creates Settings object""" + def __init__(self, data): - self.reg_season_count = data['scheduleSettings']['matchupPeriodCount'] - self.matchup_periods = data['scheduleSettings']['matchupPeriods'] - self.veto_votes_required = data['tradeSettings']['vetoVotesRequired'] - self.team_count = data['size'] - self.playoff_team_count = data['scheduleSettings']['playoffTeamCount'] - self.keeper_count = data['draftSettings']['keeperCount'] + self.reg_season_count = data["scheduleSettings"]["matchupPeriodCount"] + self.matchup_periods = data["scheduleSettings"]["matchupPeriods"] + self.veto_votes_required = data["tradeSettings"]["vetoVotesRequired"] + self.team_count = data["size"] + self.playoff_team_count = data["scheduleSettings"]["playoffTeamCount"] + self.keeper_count = data["draftSettings"]["keeperCount"] self.trade_deadline = 0 self.division_map = {} - if 'deadlineDate' in data['tradeSettings']: - self.trade_deadline = data['tradeSettings']['deadlineDate'] - self.name = data['name'] - self.tie_rule = data['scoringSettings']['matchupTieRule'] - self.playoff_tie_rule = data['scoringSettings']['playoffMatchupTieRule'] - self.playoff_matchup_period_length = data.get('scheduleSettings', {}).get('playoffMatchupPeriodLength', 0) - self.playoff_seed_tie_rule = data['scheduleSettings']['playoffSeedingRule'] - self.scoring_type = data.get('scoringSettings', {}).get('scoringType') - self.median_scoring = data.get('scoringSettings', {}).get('scoringEnhancementType') == 'WIN_BONUS_TOP_HALF' - self._raw_scoring_settings = data.get('scoringSettings', {}) - self._raw_schedule_settings = data.get('scheduleSettings', {}) - self.faab = data['acquisitionSettings']['isUsingAcquisitionBudget'] - self.acquisition_budget = data.get('acquisitionSettings', {}).get('acquisitionBudget', 0) - self.acquisition_limit = data.get('acquisitionSettings', {}).get('acquisitionLimit') - self.matchup_acquisition_limit = data.get('acquisitionSettings', {}).get('matchupAcquisitionLimit') - self.matchup_limit_per_scoring_period = data.get('acquisitionSettings', {}).get('matchupLimitPerScoringPeriod') - self.minimum_bid = data.get('acquisitionSettings', {}).get('minimumBid', 0) - self.waiver_process_days = list(data.get('acquisitionSettings', {}).get('waiverProcessDays', [])) - self.waiver_process_hour = data.get('acquisitionSettings', {}).get('waiverProcessHour') - self.trade_revision_hours = data.get('tradeSettings', {}).get('revisionHours') - divisions = data.get('scheduleSettings', {}).get('divisions', []) - for division in divisions: self.division_map[division.get('id', 0)] = division.get('name') + if "deadlineDate" in data["tradeSettings"]: + self.trade_deadline = data["tradeSettings"]["deadlineDate"] + self.name = data["name"] + self.tie_rule = data["scoringSettings"]["matchupTieRule"] + self.playoff_tie_rule = data["scoringSettings"]["playoffMatchupTieRule"] + self.playoff_matchup_period_length = data.get("scheduleSettings", {}).get( + "playoffMatchupPeriodLength", 0 + ) + self.playoff_seed_tie_rule = data["scheduleSettings"]["playoffSeedingRule"] + self.scoring_type = data.get("scoringSettings", {}).get("scoringType") + self.median_scoring = ( + data.get("scoringSettings", {}).get("scoringEnhancementType") + == "WIN_BONUS_TOP_HALF" + ) + self._raw_scoring_settings = data.get("scoringSettings", {}) + self._raw_schedule_settings = data.get("scheduleSettings", {}) + self.faab = data["acquisitionSettings"]["isUsingAcquisitionBudget"] + self.acquisition_budget = data.get("acquisitionSettings", {}).get( + "acquisitionBudget", 0 + ) + self.acquisition_limit = data.get("acquisitionSettings", {}).get( + "acquisitionLimit" + ) + self.matchup_acquisition_limit = data.get("acquisitionSettings", {}).get( + "matchupAcquisitionLimit" + ) + self.matchup_limit_per_scoring_period = data.get("acquisitionSettings", {}).get( + "matchupLimitPerScoringPeriod" + ) + self.minimum_bid = data.get("acquisitionSettings", {}).get("minimumBid", 0) + self.waiver_process_days = list( + data.get("acquisitionSettings", {}).get("waiverProcessDays", []) + ) + self.waiver_process_hour = data.get("acquisitionSettings", {}).get( + "waiverProcessHour" + ) + self.trade_revision_hours = data.get("tradeSettings", {}).get("revisionHours") + divisions = data.get("scheduleSettings", {}).get("divisions", []) + for division in divisions: + self.division_map[division.get("id", 0)] = division.get("name") def __repr__(self): - return f'Settings({self.name})' \ No newline at end of file + return f"Settings({self.name})" diff --git a/espn_api/baseball/__init__.py b/espn_api/baseball/__init__.py index 5a8822063..70a1ac3c2 100644 --- a/espn_api/baseball/__init__.py +++ b/espn_api/baseball/__init__.py @@ -1,11 +1,12 @@ -__all__ = ['League', - 'Team', - 'Player', - 'Matchup', - 'Transaction', - 'TransactionItem', - 'Settings', - ] +__all__ = [ + "League", + "Team", + "Player", + "Matchup", + "Transaction", + "TransactionItem", + "Settings", +] from .league import League from .team import Team diff --git a/espn_api/baseball/activity.py b/espn_api/baseball/activity.py index 439a6175c..affc1d024 100644 --- a/espn_api/baseball/activity.py +++ b/espn_api/baseball/activity.py @@ -1,30 +1,26 @@ from .constant import ACTIVITY_MAP + class Activity(object): def __init__(self, data, player_map, get_team_data): - self.actions = [] # List of tuples (Team, action, player) - self.date = data['date'] - for msg in data['messages']: - team = '' - action = 'UNKNOWN' - player = '' - msg_id = msg['messageTypeId'] + self.actions = [] # List of tuples (Team, action, player) + self.date = data["date"] + for msg in data["messages"]: + team = "" + action = "UNKNOWN" + player = "" + msg_id = msg["messageTypeId"] if msg_id == 244: - team = get_team_data(msg['from']) + team = get_team_data(msg["from"]) elif msg_id == 239: - team = get_team_data(msg['for']) + team = get_team_data(msg["for"]) else: - team = get_team_data(msg['to']) + team = get_team_data(msg["to"]) if msg_id in ACTIVITY_MAP: action = ACTIVITY_MAP[msg_id] - if msg['targetId'] in player_map: - player = player_map[msg['targetId']] + if msg["targetId"] in player_map: + player = player_map[msg["targetId"]] self.actions.append((team, action, player)) - - def __repr__(self): - return 'Activity(' + ' '.join("(%s,%s,%s)" % tup for tup in self.actions) + ')' - - - - + def __repr__(self): + return "Activity(" + " ".join("(%s,%s,%s)" % tup for tup in self.actions) + ")" diff --git a/espn_api/baseball/box_player.py b/espn_api/baseball/box_player.py index 2e6f32667..ee25b3906 100644 --- a/espn_api/baseball/box_player.py +++ b/espn_api/baseball/box_player.py @@ -4,32 +4,39 @@ class BoxPlayer(Player): - '''player with extra data from a matchup''' + """player with extra data from a matchup""" + def __init__(self, data, pro_schedule, week, year): super(BoxPlayer, self).__init__(data, year) - self.slot_position = 'FA' - self.pro_opponent = "None" # professional team playing against - self.pro_pos_rank = 0 # rank of professional team against player position - self.game_played = 100 # 0-100 for percent of game played + self.slot_position = "FA" + self.pro_opponent = "None" # professional team playing against + self.pro_pos_rank = 0 # rank of professional team against player position + self.game_played = 100 # 0-100 for percent of game played self.on_bye_week = False - if 'lineupSlotId' in data: - self.slot_position = POSITION_MAP[data['lineupSlotId']] + if "lineupSlotId" in data: + self.slot_position = POSITION_MAP[data["lineupSlotId"]] - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] - if player['proTeamId'] in pro_schedule: - (opp_id, date) = pro_schedule[player['proTeamId']] - self.game_date = datetime.fromtimestamp(date/1000.0) - self.game_played = 100 if datetime.now() > self.game_date + timedelta(hours=3) else 0 + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) + if player["proTeamId"] in pro_schedule: + opp_id, date = pro_schedule[player["proTeamId"]] + self.game_date = datetime.fromtimestamp(date / 1000.0) + self.game_played = ( + 100 if datetime.now() > self.game_date + timedelta(hours=3) else 0 + ) self.pro_opponent = PRO_TEAM_MAP[opp_id] - else: # bye week + else: # bye week self.on_bye_week = True stats = self.stats.get(week, {}) - self.points = stats.get('points', 0) - self.points_breakdown = stats.get('breakdown', 0) - self.projected_points = stats.get('projected_points', 0) - self.projected_breakdown = stats.get('projected_breakdown', 0) + self.points = stats.get("points", 0) + self.points_breakdown = stats.get("breakdown", 0) + self.projected_points = stats.get("projected_points", 0) + self.projected_breakdown = stats.get("projected_breakdown", 0) def __repr__(self): - return f'Player({self.name}, points:{self.points}, projected:{self.projected_points})' + return f"Player({self.name}, points:{self.points}, projected:{self.projected_points})" diff --git a/espn_api/baseball/box_score.py b/espn_api/baseball/box_score.py index b837ab643..fe6aa44c5 100644 --- a/espn_api/baseball/box_score.py +++ b/espn_api/baseball/box_score.py @@ -3,15 +3,17 @@ from .constant import STATS_MAP + class BoxScore(ABC): - ''' ''' + """ """ + def __init__(self, data): - self.winner = data['winner'] - - self._process_team(data['home'], True) + self.winner = data["winner"] - if 'away' in data: - self._process_team(data['away'], False) + self._process_team(data["home"], True) + + if "away" in data: + self._process_team(data["away"], False) else: self._process_team(None, False) @@ -20,22 +22,23 @@ def _process_team(self, team_data, is_home_team): team = {} if team_data is not None: - team['id'] = team_data['teamId'] + team["id"] = team_data["teamId"] if is_home_team: - self.home_team = team['id'] + self.home_team = team["id"] else: - self.away_team = team.get('id') - + self.away_team = team.get("id") + def __repr__(self): away_team = self.away_team or "BYE" home_team = self.home_team or "BYE" - return f'Box Score({away_team} at {home_team})' + return f"Box Score({away_team} at {home_team})" class H2HCategoryBoxScore(BoxScore): - '''Boxscore class for head to head categories leagues''' - def __init__(self, data, pro_schedule, year, scoring_period = 0): + """Boxscore class for head to head categories leagues""" + + def __init__(self, data, pro_schedule, year, scoring_period=0): super().__init__(data) def _process_team(self, team_data, is_home_team): @@ -44,61 +47,77 @@ def _process_team(self, team_data, is_home_team): team = {} if team_data is not None: - team['wins'] = team_data['cumulativeScore']['wins'] - team['losses'] = team_data['cumulativeScore']['losses'] - team['ties'] = team_data['cumulativeScore']['ties'] - - team['stats'] = {} - for stat_key, stat_dict in team_data['cumulativeScore']['scoreByStat'].items(): - team['stats'][STATS_MAP[int(stat_key)]] = { - 'value': stat_dict['score'], - 'result': stat_dict['result'] + team["wins"] = team_data["cumulativeScore"]["wins"] + team["losses"] = team_data["cumulativeScore"]["losses"] + team["ties"] = team_data["cumulativeScore"]["ties"] + + team["stats"] = {} + for stat_key, stat_dict in team_data["cumulativeScore"][ + "scoreByStat" + ].items(): + team["stats"][STATS_MAP[int(stat_key)]] = { + "value": stat_dict["score"], + "result": stat_dict["result"], } - + if is_home_team: - self.home_wins = team['wins'] - self.home_losses = team['losses'] - self.home_ties = team['ties'] - self.home_stats = team['stats'] + self.home_wins = team["wins"] + self.home_losses = team["losses"] + self.home_ties = team["ties"] + self.home_stats = team["stats"] else: - self.away_wins = team.get('wins') - self.away_losses = team.get('losses') - self.away_ties = team.get('ties') - self.away_stats = team.get('stats') + self.away_wins = team.get("wins") + self.away_losses = team.get("losses") + self.away_ties = team.get("ties") + self.away_stats = team.get("stats") class H2HPointsBoxScore(BoxScore): - '''Boxscore class for head to head points leagues''' - def __init__(self, data, pro_schedule, year, scoring_period = 0): + """Boxscore class for head to head points leagues""" + + def __init__(self, data, pro_schedule, year, scoring_period=0): super().__init__(data) - (self.home_team, self.home_score, self.home_projected, self.home_lineup) = self._get_team_data('home', data, pro_schedule, scoring_period, year) + self.home_team, self.home_score, self.home_projected, self.home_lineup = ( + self._get_team_data("home", data, pro_schedule, scoring_period, year) + ) - (self.away_team, self.away_score, self.away_projected, self.away_lineup) = self._get_team_data('away', data, pro_schedule, scoring_period, year) + self.away_team, self.away_score, self.away_projected, self.away_lineup = ( + self._get_team_data("away", data, pro_schedule, scoring_period, year) + ) def _process_team(self, team_data, is_home_team): super()._process_team(team_data, is_home_team) # TODO implement setting the scores def _get_team_data(self, team, data, pro_schedule, week, year): - if team not in data: - return (0, 0, -1, []) # -1 projected score indicates no projection available (bye week / missing) - - team_id = data[team]['teamId'] - team_projected = -1 - if 'totalPointsLive' in data[team]: - team_score = round(data[team]['totalPointsLive'], 2) - team_projected = round(data[team].get('totalProjectedPointsLive', -1), 2) - else: - team_score = round(data[team]['totalPoints'], 2) - team_roster = data[team].get('rosterForCurrentScoringPeriod', {}).get('entries', []) - team_lineup = [BoxPlayer(player, pro_schedule, week, year) for player in team_roster] + if team not in data: + return ( + 0, + 0, + -1, + [], + ) # -1 projected score indicates no projection available (bye week / missing) + + team_id = data[team]["teamId"] + team_projected = -1 + if "totalPointsLive" in data[team]: + team_score = round(data[team]["totalPointsLive"], 2) + team_projected = round(data[team].get("totalProjectedPointsLive", -1), 2) + else: + team_score = round(data[team]["totalPoints"], 2) + team_roster = ( + data[team].get("rosterForCurrentScoringPeriod", {}).get("entries", []) + ) + team_lineup = [ + BoxPlayer(player, pro_schedule, week, year) for player in team_roster + ] - return (team_id, team_score, team_projected, team_lineup) + return (team_id, team_score, team_projected, team_lineup) class RotoBoxScore(BoxScore): - '''Boxscore for rotisserie (ROTO) leagues. + """Boxscore for rotisserie (ROTO) leagues. In roto there is no home/away matchup — all teams accumulate stats and are ranked against each other in each category. One RotoBoxScore is returned @@ -117,7 +136,8 @@ class RotoBoxScore(BoxScore): - total_points (float): cumulative roto points (sum of category ranks) - stats : dict mapping stat name → {'score': float, 'rank': float} - lineup : list[BoxPlayer] for the current scoring period - ''' + """ + def __init__(self, data, pro_schedule, year, scoring_period=0): # Skip BoxScore.__init__ — roto data has no winner/home/away keys. # Set the inherited attributes to None so callers can still access them. @@ -125,34 +145,42 @@ def __init__(self, data, pro_schedule, year, scoring_period=0): self.home_team = None self.away_team = None - self.matchup_period = data.get('matchupPeriodId') + self.matchup_period = data.get("matchupPeriodId") self.teams = [] - for team_data in data.get('teams', []): - team_id = team_data['teamId'] - live = team_data.get('totalPointsLive') - total = round(live if live is not None else team_data.get('totalPoints', 0), 2) + for team_data in data.get("teams", []): + team_id = team_data["teamId"] + live = team_data.get("totalPointsLive") + total = round( + live if live is not None else team_data.get("totalPoints", 0), 2 + ) stats = {} - for stat_id_str, stat_dict in team_data.get('cumulativeScore', {}).get('scoreByStat', {}).items(): - stat_name = STATS_MAP.get(int(stat_id_str), f'stat_{stat_id_str}') - score = stat_dict['score'] + for stat_id_str, stat_dict in ( + team_data.get("cumulativeScore", {}).get("scoreByStat", {}).items() + ): + stat_name = STATS_MAP.get(int(stat_id_str), f"stat_{stat_id_str}") + score = stat_dict["score"] # ESPN sends the literal string 'Infinity' for rate stats (e.g. WHIP, # ERA) when the denominator is zero — coerce to float('inf') so # callers can do arithmetic/comparisons without type-switching. - if score == 'Infinity': - score = float('inf') - stats[stat_name] = {'score': score, 'rank': stat_dict['rank']} + if score == "Infinity": + score = float("inf") + stats[stat_name] = {"score": score, "rank": stat_dict["rank"]} - entries = team_data.get('rosterForCurrentScoringPeriod', {}).get('entries', []) + entries = team_data.get("rosterForCurrentScoringPeriod", {}).get( + "entries", [] + ) lineup = [BoxPlayer(p, pro_schedule, scoring_period, year) for p in entries] - self.teams.append({ - 'team': team_id, - 'total_points': total, - 'stats': stats, - 'lineup': lineup, - }) + self.teams.append( + { + "team": team_id, + "total_points": total, + "stats": stats, + "lineup": lineup, + } + ) def _process_team(self, team_data, is_home_team): # Roto has no home/away concept — this abstract method is required by @@ -161,4 +189,4 @@ def _process_team(self, team_data, is_home_team): pass def __repr__(self): - return f'Roto Box Score(period:{self.matchup_period})' + return f"Roto Box Score(period:{self.matchup_period})" diff --git a/espn_api/baseball/constant.py b/espn_api/baseball/constant.py index 552d07770..2d08a27ab 100644 --- a/espn_api/baseball/constant.py +++ b/espn_api/baseball/constant.py @@ -1,95 +1,95 @@ # Maps defaultPositionId (a player's natural position) to a position string. # This is a DIFFERENT numbering system from lineupSlotId (see POSITION_MAP). DEFAULT_POSITION_MAP = { - 1: 'SP', - 2: 'C', - 3: '1B', - 4: '2B', - 5: '3B', - 6: 'SS', - 7: 'LF', - 8: 'CF', - 9: 'RF', - 10: 'DH', - 11: 'RP', + 1: "SP", + 2: "C", + 3: "1B", + 4: "2B", + 5: "3B", + 6: "SS", + 7: "LF", + 8: "CF", + 9: "RF", + 10: "DH", + 11: "RP", } # Maps lineupSlotId (where a player sits in the lineup) to a slot string. POSITION_MAP = { - 0: 'C', - 1: '1B', - 2: '2B', - 3: '3B', - 4: 'SS', - 5: 'OF', - 6: '2B/SS', - 7: '1B/3B', - 8: 'LF', - 9: 'CF', - 10: 'RF', - 11: 'DH', - 12: 'UTIL', - 13: 'P', - 14: 'SP', - 15: 'RP', - 16: 'BE', - 17: 'IL', - 19: 'IF', # 1B/2B/SS/3B + 0: "C", + 1: "1B", + 2: "2B", + 3: "3B", + 4: "SS", + 5: "OF", + 6: "2B/SS", + 7: "1B/3B", + 8: "LF", + 9: "CF", + 10: "RF", + 11: "DH", + 12: "UTIL", + 13: "P", + 14: "SP", + 15: "RP", + 16: "BE", + 17: "IL", + 19: "IF", # 1B/2B/SS/3B # 18, 21, 22 have appeared but unknown what position they correspond to # reverse mapping — used by free_agents() to translate position name → slot ID filter - 'C': 0, - '1B': 1, - '2B': 2, - '3B': 3, - 'SS': 4, - 'OF': 5, - '2B/SS': 6, - '1B/3B': 7, - 'LF': 8, - 'CF': 9, - 'RF': 10, - 'DH': 11, - 'UTIL': 12, - 'P': 13, - 'SP': 14, - 'RP': 15, - 'BE': 16, - 'IL': 17, - 'IF': 19, + "C": 0, + "1B": 1, + "2B": 2, + "3B": 3, + "SS": 4, + "OF": 5, + "2B/SS": 6, + "1B/3B": 7, + "LF": 8, + "CF": 9, + "RF": 10, + "DH": 11, + "UTIL": 12, + "P": 13, + "SP": 14, + "RP": 15, + "BE": 16, + "IL": 17, + "IF": 19, } PRO_TEAM_MAP = { - 0: 'FA', - 1: 'Bal', - 2: 'Bos', - 3: 'LAA', - 4: 'ChW', - 5: 'Cle', - 6: 'Det', - 7: 'KC', - 8: 'Mil', - 9: 'Min', - 10: 'NYY', - 11: 'Oak', - 12: 'Sea', - 13: 'Tex', - 14: 'Tor', - 15: 'Atl', - 16: 'ChC', - 17: 'Cin', - 18: 'Hou', - 19: 'LAD', - 20: 'Wsh', - 21: 'NYM', - 22: 'Phi', - 23: 'Pit', - 24: 'StL', - 25: 'SD', - 26: 'SF', - 27: 'Col', - 28: 'Mia', - 29: 'Ari', - 30: 'TB', + 0: "FA", + 1: "Bal", + 2: "Bos", + 3: "LAA", + 4: "ChW", + 5: "Cle", + 6: "Det", + 7: "KC", + 8: "Mil", + 9: "Min", + 10: "NYY", + 11: "Oak", + 12: "Sea", + 13: "Tex", + 14: "Tor", + 15: "Atl", + 16: "ChC", + 17: "Cin", + 18: "Hou", + 19: "LAD", + 20: "Wsh", + 21: "NYM", + 22: "Phi", + 23: "Pit", + 24: "StL", + 25: "SD", + 26: "SF", + 27: "Col", + 28: "Mia", + 29: "Ari", + 30: "TB", } # where batter and pitcher stats have the same abbreviation and both are commonly used @@ -97,136 +97,224 @@ # P_ = pitcher stat STATS_MAP = { - 0: 'AB', - 1: 'H', - 2: 'AVG', - 3: '2B', - 4: '3B', - 5: 'HR', - 6: 'XBH', # 2B + 3B + HR - 7: '1B', - 8: 'TB', # 1 * COUNT(1B) + 2 * COUNT(2B) + 3 * COUNT(3B) + 4 * COUNT(HR) - 9: 'SLG', - 10: 'B_BB', - 11: 'B_IBB', - 12: 'HBP', - 13: 'SF', # Sacrifice Fly - 14: 'SH', # Sacrifice Hit - i.e. Sacrifice Bunt - 15: 'SAC', # total sacrifices = SF + SH - 16: 'PA', - 17: 'OBP', - 18: 'OPS', # OBP + SLG - 19: 'RC', # Runs Created = TB * (H + BB) / (AB + BB) - 20: 'R', - 21: 'RBI', + 0: "AB", + 1: "H", + 2: "AVG", + 3: "2B", + 4: "3B", + 5: "HR", + 6: "XBH", # 2B + 3B + HR + 7: "1B", + 8: "TB", # 1 * COUNT(1B) + 2 * COUNT(2B) + 3 * COUNT(3B) + 4 * COUNT(HR) + 9: "SLG", + 10: "B_BB", + 11: "B_IBB", + 12: "HBP", + 13: "SF", # Sacrifice Fly + 14: "SH", # Sacrifice Hit - i.e. Sacrifice Bunt + 15: "SAC", # total sacrifices = SF + SH + 16: "PA", + 17: "OBP", + 18: "OPS", # OBP + SLG + 19: "RC", # Runs Created = TB * (H + BB) / (AB + BB) + 20: "R", + 21: "RBI", # 22: '', - 23: 'SB', - 24: 'CS', - 25: 'SB-CS', # net steals - 26: 'GDP', - 27: 'B_SO', # batter strike-outs - 28: 'PS', # pitches seen - 29: 'PPA', # pitches per plate appearance = PS / PA + 23: "SB", + 24: "CS", + 25: "SB-CS", # net steals + 26: "GDP", + 27: "B_SO", # batter strike-outs + 28: "PS", # pitches seen + 29: "PPA", # pitches per plate appearance = PS / PA # 30: '', - 31: 'CYC', - 32: 'GP', # pitcher games pitched - 33: 'GS', # games started - 34: 'OUTS', # divide by 3 for IP - 35: 'TBF', - 36: 'P', # pitches - 37: 'P_H', - 38: 'OBA', # Opponent Batting Average - 39: 'P_BB', - 40: 'P_IBB', # intentional walks allowed - 41: 'WHIP', - 42: 'HBP', - 43: 'OOBP', # Opponent On-Base Percentage - 44: 'P_R', - 45: 'ER', - 46: 'P_HR', - 47: 'ERA', - 48: 'K', - 49: 'K/9', - 50: 'WP', - 51: 'BLK', - 52: 'PK', # pickoff - 53: 'W', - 54: 'L', - 55: 'WPCT', # Win Percentage - 56: 'SVO', # Save opportunity - 57: 'SV', - 58: 'BLSV', # BLown SaVe - 59: 'SV%', # Save percentage - 60: 'HLD', + 31: "CYC", + 32: "GP", # pitcher games pitched + 33: "GS", # games started + 34: "OUTS", # divide by 3 for IP + 35: "TBF", + 36: "P", # pitches + 37: "P_H", + 38: "OBA", # Opponent Batting Average + 39: "P_BB", + 40: "P_IBB", # intentional walks allowed + 41: "WHIP", + 42: "HBP", + 43: "OOBP", # Opponent On-Base Percentage + 44: "P_R", + 45: "ER", + 46: "P_HR", + 47: "ERA", + 48: "K", + 49: "K/9", + 50: "WP", + 51: "BLK", + 52: "PK", # pickoff + 53: "W", + 54: "L", + 55: "WPCT", # Win Percentage + 56: "SVO", # Save opportunity + 57: "SV", + 58: "BLSV", # BLown SaVe + 59: "SV%", # Save percentage + 60: "HLD", # 61: '', - 62: 'CG', - 63: 'QS', # Quality Starts + 62: "CG", + 63: "QS", # Quality Starts # 64: '', - 65: 'NH', # No-hitters - 66: 'PG', # Perfect Games - 67: 'TC', # Total Chances = PO + A + E - 68: 'PO', # Put Outs - 69: 'A', # Assists - 70: 'OFA', # Outfield Assists - 71: 'FPCT', # Fielding Percentage - 72: 'E', - 73: 'DP', # Double plays turned + 65: "NH", # No-hitters + 66: "PG", # Perfect Games + 67: "TC", # Total Chances = PO + A + E + 68: "PO", # Put Outs + 69: "A", # Assists + 70: "OFA", # Outfield Assists + 71: "FPCT", # Fielding Percentage + 72: "E", + 73: "DP", # Double plays turned # Not sure what to call the next four # 74 is games played where the batter's team won # 75 is the same except when the team lost # 76 and 77 are the same except for pitchers - 74: 'B_G_W', - 75: 'B_G_L', - 76: 'P_G_W', - 77: 'P_G_L', + 74: "B_G_W", + 75: "B_G_L", + 76: "P_G_W", + 77: "P_G_L", # 78: , # 79: , # 80: , - 81: 'G', # Games Played - 82: 'K/BB', # Strikeout to Walk Ratio - 83: 'SVHD', # Saves + Holds - 99: 'STARTER', + 81: "G", # Games Played + 82: "K/BB", # Strikeout to Walk Ratio + 83: "SVHD", # Saves + Holds + 99: "STARTER", } STAT_SPLIT_MAP = { - 0: 'season', - 1: 'last_7', - 2: 'last_15', - 3: 'last_30', - 5: 'box_score', + 0: "season", + 1: "last_7", + 2: "last_15", + 3: "last_30", + 5: "box_score", } # Stat IDs that apply only to pitchers PITCHER_ONLY_STATS = { - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 62, 63, 65, 66, 76, 77, 82, 83 + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 62, + 63, + 65, + 66, + 76, + 77, + 82, + 83, } # Stat IDs that apply only to batters (excludes shared fielding stats and games played) BATTER_ONLY_STATS = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 23, 24, 25, 26, 27, 28, 29, 31, 74, 75 + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 31, + 74, + 75, } # Eligibility-slot names used to classify a player as a pitcher or batter # when filtering stats by role (e.g. dropping batter-only stats from a pitcher). -PITCHER_POSITIONS = {'SP', 'RP', 'P'} -BATTER_POSITIONS = {'C', '1B', '2B', '3B', 'SS', 'OF', 'LF', 'CF', 'RF', 'DH', 'UTIL', '2B/SS', '1B/3B'} +PITCHER_POSITIONS = {"SP", "RP", "P"} +BATTER_POSITIONS = { + "C", + "1B", + "2B", + "3B", + "SS", + "OF", + "LF", + "CF", + "RF", + "DH", + "UTIL", + "2B/SS", + "1B/3B", +} ACTIVITY_MAP = { - 178: 'FA ADDED', - 180: 'WAIVER ADDED', - 179: 'DROPPED', - 181: 'DROPPED', - 239: 'DROPPED', - 244: 'TRADED', - 'FA': 178, - 'WAIVER': 180, - 'TRADED': 244 + 178: "FA ADDED", + 180: "WAIVER ADDED", + 179: "DROPPED", + 181: "DROPPED", + 239: "DROPPED", + 244: "TRADED", + "FA": 178, + "WAIVER": 180, + "TRADED": 244, } TRANSACTION_TYPES = { - 'DRAFT', 'TRADE_ACCEPT', 'WAIVER', 'TRADE_VETO', 'FUTURE_ROSTER', - 'ROSTER', 'RETRO_ROSTER', 'TRADE_PROPOSAL', 'TRADE_UPHOLD', - 'FREEAGENT', 'TRADE_DECLINE', 'WAIVER_ERROR', 'TRADE_ERROR' + "DRAFT", + "TRADE_ACCEPT", + "WAIVER", + "TRADE_VETO", + "FUTURE_ROSTER", + "ROSTER", + "RETRO_ROSTER", + "TRADE_PROPOSAL", + "TRADE_UPHOLD", + "FREEAGENT", + "TRADE_DECLINE", + "WAIVER_ERROR", + "TRADE_ERROR", } diff --git a/espn_api/baseball/league.py b/espn_api/baseball/league.py index 9992d56b9..601aa4258 100644 --- a/espn_api/baseball/league.py +++ b/espn_api/baseball/league.py @@ -14,15 +14,37 @@ from .settings import Settings from .transaction import Transaction -class League(BaseLeague): - '''Creates a League instance for Public/Private ESPN league''' - - ScoreTypes = {'H2H_CATEGORY': H2HCategoryBoxScore, 'H2H_POINTS': H2HPointsBoxScore, 'ROTO': RotoBoxScore} - def __init__(self, league_id: int, year: int, espn_s2=None, swid=None, fetch_league=True, debug=False): - super().__init__(league_id=league_id, year=year, sport='mlb', espn_s2=espn_s2, swid=swid, debug=debug) - - self._set_scoring_class = lambda scoring_type: League.ScoreTypes.get(scoring_type, BoxScore) +class League(BaseLeague): + """Creates a League instance for Public/Private ESPN league""" + + ScoreTypes = { + "H2H_CATEGORY": H2HCategoryBoxScore, + "H2H_POINTS": H2HPointsBoxScore, + "ROTO": RotoBoxScore, + } + + def __init__( + self, + league_id: int, + year: int, + espn_s2=None, + swid=None, + fetch_league=True, + debug=False, + ): + super().__init__( + league_id=league_id, + year=year, + sport="mlb", + espn_s2=espn_s2, + swid=swid, + debug=debug, + ) + + self._set_scoring_class = lambda scoring_type: League.ScoreTypes.get( + scoring_type, BoxScore + ) self.scoring_type = None self._box_score_class = None @@ -34,7 +56,7 @@ def __init__(self, league_id: int, year: int, espn_s2=None, swid=None, fetch_lea def fetch_league(self): data = self._fetch_league() - self.scoring_type = data['settings']['scoringSettings']['scoringType'] + self.scoring_type = data["settings"]["scoringSettings"]["scoringType"] self._fetch_teams(data) self._box_score_class = self._set_scoring_class(self.scoring_type) super()._fetch_draft() @@ -45,12 +67,12 @@ def _fetch_league(self): return data def _fetch_teams(self, data): - '''Fetch teams in league''' + """Fetch teams in league""" super()._fetch_teams(data, TeamClass=Team) # replace opponentIds in schedule with team instances for team in self.teams: - team.division_name = self.settings.division_map.get(team.division_id, '') + team.division_name = self.settings.division_map.get(team.division_id, "") for week, matchup in enumerate(team.schedule): for opponent in self.teams: if matchup.away_team == opponent.team_id: @@ -59,20 +81,28 @@ def _fetch_teams(self, data): matchup.home_team = opponent def standings(self) -> List[Team]: - standings = sorted(self.teams, key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, reverse=False) + standings = sorted( + self.teams, + key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, + reverse=False, + ) return standings def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: - '''Returns list of matchups for a given matchup period''' + """Returns list of matchups for a given matchup period""" if not matchupPeriod: - matchupPeriod=self.currentMatchupPeriod + matchupPeriod = self.currentMatchupPeriod params = { - 'view': 'mMatchup', + "view": "mMatchup", } data = self.espn_request.league_get(params=params) - schedule = data['schedule'] - matchups = [Matchup(matchup) for matchup in schedule if matchup['matchupPeriodId'] == matchupPeriod] + schedule = data["schedule"] + matchups = [ + Matchup(matchup) + for matchup in schedule + if matchup["matchupPeriodId"] == matchupPeriod + ] for team in self.teams: for matchup in matchups: @@ -83,56 +113,80 @@ def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: return matchups - def transactions(self, types: Set[str] = None, scoring_period: int = None) -> List[Transaction]: - '''Returns a list of transactions for a given scoring period''' + def transactions( + self, types: Set[str] = None, scoring_period: int = None + ) -> List[Transaction]: + """Returns a list of transactions for a given scoring period""" if types is None: types = TRANSACTION_TYPES for t in types: if t not in TRANSACTION_TYPES: - raise ValueError(f'Invalid transaction type: {t}. Valid types: {TRANSACTION_TYPES}') + raise ValueError( + f"Invalid transaction type: {t}. Valid types: {TRANSACTION_TYPES}" + ) if scoring_period is None: scoring_period = self.scoringPeriodId params = { - 'view': 'mTransactions2', - 'scoringPeriodId': scoring_period, + "view": "mTransactions2", + "scoringPeriodId": scoring_period, } - filters = {'transactions': {'filterType': {'value': list(types)}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = {"transactions": {"filterType": {"value": list(types)}}} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) return [ Transaction(t, self.player_map, self.get_team_data) - for t in data.get('transactions', []) + for t in data.get("transactions", []) ] - def recent_activity(self, size: int = 25, msg_type: str = None, offset: int = 0) -> List[Activity]: - '''Returns a list of recent league activities (Add, Drop, Trade)''' + def recent_activity( + self, size: int = 25, msg_type: str = None, offset: int = 0 + ) -> List[Activity]: + """Returns a list of recent league activities (Add, Drop, Trade)""" if self.year < 2019: - raise Exception('Cant use recent activity before 2019') + raise Exception("Cant use recent activity before 2019") - msg_types = [178,180,179,239,181,244] + msg_types = [178, 180, 179, 239, 181, 244] if msg_type in ACTIVITY_MAP: msg_types = [ACTIVITY_MAP[msg_type]] - params = { - 'view': 'kona_league_communication' + params = {"view": "kona_league_communication"} + + filters = { + "topics": { + "filterType": {"value": ["ACTIVITY_TRANSACTIONS"]}, + "limit": size, + "limitPerMessageSet": {"value": 25}, + "offset": offset, + "sortMessageDate": {"sortPriority": 1, "sortAsc": False}, + "sortFor": {"sortPriority": 2, "sortAsc": False}, + "filterIncludeMessageTypeIds": {"value": msg_types}, + } } - - filters = {"topics":{"filterType":{"value":["ACTIVITY_TRANSACTIONS"]},"limit":size,"limitPerMessageSet":{"value":25},"offset":offset,"sortMessageDate":{"sortPriority":1,"sortAsc":False},"sortFor":{"sortPriority":2,"sortAsc":False},"filterIncludeMessageTypeIds":{"value":msg_types}}} - headers = {'x-fantasy-filter': json.dumps(filters)} - data = self.espn_request.league_get(extend='/communication/', params=params, headers=headers) - data = data['topics'] - activity = [Activity(topic, self.player_map, self.get_team_data) for topic in data] + headers = {"x-fantasy-filter": json.dumps(filters)} + data = self.espn_request.league_get( + extend="/communication/", params=params, headers=headers + ) + data = data["topics"] + activity = [ + Activity(topic, self.player_map, self.get_team_data) for topic in data + ] return activity - def free_agents(self, week: int=None, size: int=50, position: str=None, position_id: int=None) -> List[Player]: - '''Returns a List of Free Agents for a Given Week\n - Should only be used with most recent season''' + def free_agents( + self, + week: int = None, + size: int = 50, + position: str = None, + position_id: int = None, + ) -> List[Player]: + """Returns a List of Free Agents for a Given Week\n + Should only be used with most recent season""" if self.year < 2019: - raise Exception('Cant use free agents before 2019') + raise Exception("Cant use free agents before 2019") if not week: week = self.current_week @@ -142,21 +196,34 @@ def free_agents(self, week: int=None, size: int=50, position: str=None, position if position_id: slot_filter.append(position_id) - params = { - 'view': 'kona_player_info', - 'scoringPeriodId': week, + "view": "kona_player_info", + "scoringPeriodId": week, + } + filters = { + "players": { + "filterStatus": {"value": ["FREEAGENT", "WAIVERS"]}, + "filterSlotIds": {"value": slot_filter}, + "limit": size, + "sortPercOwned": {"sortPriority": 1, "sortAsc": False}, + "sortDraftRanks": { + "sortPriority": 100, + "sortAsc": True, + "value": "STANDARD", + }, + } } - filters = {"players":{"filterStatus":{"value":["FREEAGENT","WAIVERS"]},"filterSlotIds":{"value":slot_filter},"limit":size,"sortPercOwned":{"sortPriority":1,"sortAsc":False},"sortDraftRanks":{"sortPriority":100,"sortAsc":True,"value":"STANDARD"}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - players = data['players'] + players = data["players"] return [Player(player, self.year) for player in players] - def box_scores(self, matchup_period: int = None, scoring_period: int = None) -> List[BoxScore]: - '''Returns list of box score for a given matchup or scoring period. + def box_scores( + self, matchup_period: int = None, scoring_period: int = None + ) -> List[BoxScore]: + """Returns list of box score for a given matchup or scoring period. The concrete type depends on the league's scoring_type: - H2H_CATEGORY → H2HCategoryBoxScore (home/away with category results) @@ -164,9 +231,9 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None) -> - ROTO → RotoBoxScore (no home/away — callers iterate the .teams list instead) All three inherit from BoxScore. - ''' + """ if self.year < 2019: - raise Exception('Cant use box score before 2019') + raise Exception("Cant use box score before 2019") matchup_id = self.currentMatchupPeriod scoring_id = self.current_week @@ -177,23 +244,26 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None) -> matchup_id = matchup_period params = { - 'view': ['mMatchupScore', 'mScoreboard'], - 'scoringPeriodId': scoring_id + "view": ["mMatchupScore", "mScoreboard"], + "scoringPeriodId": scoring_id, } - filters = {"schedule":{"filterMatchupPeriodIds":{"value":[matchup_id]}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = {"schedule": {"filterMatchupPeriodIds": {"value": [matchup_id]}}} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) pro_schedule = self._get_pro_schedule(scoring_id) - schedule = data['schedule'] - box_data = [self._box_score_class(matchup, pro_schedule, self.year, scoring_id) for matchup in schedule] + schedule = data["schedule"] + box_data = [ + self._box_score_class(matchup, pro_schedule, self.year, scoring_id) + for matchup in schedule + ] team_map = {t.team_id: t for t in self.teams} for matchup in box_data: - if self.scoring_type == 'ROTO': + if self.scoring_type == "ROTO": for entry in matchup.teams: - entry['team'] = team_map.get(entry['team'], entry['team']) + entry["team"] = team_map.get(entry["team"], entry["team"]) else: if matchup.home_team in team_map: matchup.home_team = team_map[matchup.home_team] @@ -201,8 +271,10 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None) -> matchup.away_team = team_map[matchup.away_team] return box_data - def player_info(self, name: str = None, playerId: Union[int, list] = None) -> Union[Player, List[Player]]: - '''Returns Player class if name or playerId found''' + def player_info( + self, name: str = None, playerId: Union[int, list] = None + ) -> Union[Player, List[Player]]: + """Returns Player class if name or playerId found""" if name and name in self.player_map: playerId = self.player_map[name] if playerId is None: @@ -211,30 +283,29 @@ def player_info(self, name: str = None, playerId: Union[int, list] = None) -> Un playerId = [playerId] split_filters = ["0{}{}".format(i, self.year) for i in range(1, 4)] - data = self.espn_request.get_player_card(playerId, self.finalScoringPeriod, additional_filters=split_filters) - if len(data['players']) == 1: - return Player(data['players'][0], self.year) - if len(data['players']) > 1: - return [Player(player, self.year) for player in data['players']] + data = self.espn_request.get_player_card( + playerId, self.finalScoringPeriod, additional_filters=split_filters + ) + if len(data["players"]) == 1: + return Player(data["players"][0], self.year) + if len(data["players"]) > 1: + return [Player(player, self.year) for player in data["players"]] def refresh(self): - '''Gets latest league data without re-fetching all players''' + """Gets latest league data without re-fetching all players""" data = super()._fetch_league(SettingsClass=Settings) - self.scoring_type = data['settings']['scoringSettings']['scoringType'] + self.scoring_type = data["settings"]["scoringSettings"]["scoringType"] self._fetch_teams(data) self._box_score_class = self._set_scoring_class(self.scoring_type) def load_roster_week(self, week: int) -> None: - '''Sets Teams Roster for a Certain Week''' - params = { - 'view': 'mRoster', - 'scoringPeriodId': week - } + """Sets Teams Roster for a Certain Week""" + params = {"view": "mRoster", "scoringPeriodId": week} data = self.espn_request.league_get(params=params) team_roster = {} - for team in data['teams']: - team_roster[team['id']] = team['roster'] + for team in data["teams"]: + team_roster[team["id"]] = team["roster"] for team in self.teams: roster = team_roster[team.team_id] diff --git a/espn_api/baseball/matchup.py b/espn_api/baseball/matchup.py index 85dcff918..917ddda43 100644 --- a/espn_api/baseball/matchup.py +++ b/espn_api/baseball/matchup.py @@ -1,7 +1,9 @@ from .constant import STATS_MAP + class Matchup(object): - '''Creates Matchup instance''' + """Creates Matchup instance""" + def __init__(self, data): self.home_team_live_score = None self.away_team_live_score = None @@ -12,25 +14,37 @@ def __repr__(self): # writing this too early to see if data['home']['totalPoints'] is final score # it might also be used for points leagues instead of category leagues if not self.away_team_live_score: - return 'Matchup(%s, %s)' % (self.home_team, self.away_team, ) + return "Matchup(%s, %s)" % ( + self.home_team, + self.away_team, + ) else: - return 'Matchup(%s %s - %s %s)' % (self.home_team, - str(round(self.home_team_live_score, 1)), - str(round(self.away_team_live_score, 1)), - self.away_team) + return "Matchup(%s %s - %s %s)" % ( + self.home_team, + str(round(self.home_team_live_score, 1)), + str(round(self.away_team_live_score, 1)), + self.away_team, + ) def _fetch_matchup_info(self, data): - '''Fetch info for matchup''' - self.home_team = data['home']['teamId'] - self.home_final_score = data['home']['totalPoints'] - self.away_team = data['away']['teamId'] - self.away_final_score = data['away']['totalPoints'] - self.winner = data['winner'] + """Fetch info for matchup""" + self.home_team = data["home"]["teamId"] + self.home_final_score = data["home"]["totalPoints"] + self.away_team = data["away"]["teamId"] + self.away_final_score = data["away"]["totalPoints"] + self.winner = data["winner"] # if stats are available - if 'cumulativeScore' in data['home'].keys() and data['home']['cumulativeScore']['scoreByStat']: + if ( + "cumulativeScore" in data["home"].keys() + and data["home"]["cumulativeScore"]["scoreByStat"] + ): - self.home_team_live_score = (data['home']['cumulativeScore']['wins'] + - data['home']['cumulativeScore']['ties']/2) - self.away_team_live_score = (data['away']['cumulativeScore']['wins'] + - data['away']['cumulativeScore']['ties']/2) + self.home_team_live_score = ( + data["home"]["cumulativeScore"]["wins"] + + data["home"]["cumulativeScore"]["ties"] / 2 + ) + self.away_team_live_score = ( + data["away"]["cumulativeScore"]["wins"] + + data["away"]["cumulativeScore"]["ties"] / 2 + ) diff --git a/espn_api/baseball/player.py b/espn_api/baseball/player.py index cb8f7bc06..865a1f3d9 100644 --- a/espn_api/baseball/player.py +++ b/espn_api/baseball/player.py @@ -1,62 +1,85 @@ from datetime import datetime from .constant import ( - DEFAULT_POSITION_MAP, POSITION_MAP, PRO_TEAM_MAP, STATS_MAP, STAT_SPLIT_MAP, - PITCHER_ONLY_STATS, BATTER_ONLY_STATS, PITCHER_POSITIONS, BATTER_POSITIONS, + DEFAULT_POSITION_MAP, + POSITION_MAP, + PRO_TEAM_MAP, + STATS_MAP, + STAT_SPLIT_MAP, + PITCHER_ONLY_STATS, + BATTER_ONLY_STATS, + PITCHER_POSITIONS, + BATTER_POSITIONS, ) from .utils import json_parsing + class Player(object): - '''Player are part of team''' + """Player are part of team""" + def __init__(self, data, year): - self.name = json_parsing(data, 'fullName') - self.playerId = json_parsing(data, 'id') - self.position = DEFAULT_POSITION_MAP.get(json_parsing(data, 'defaultPositionId'), str(json_parsing(data, 'defaultPositionId'))) - self.lineupSlot = POSITION_MAP.get(data.get('lineupSlotId'), '') - self.eligibleSlots = [POSITION_MAP.get(pos, pos) for pos in json_parsing(data, 'eligibleSlots')] # if position isn't in position map, just use the position id number - self.acquisitionType = json_parsing(data, 'acquisitionType') - raw_acq_date = json_parsing(data, 'acquisitionDate') - self.acquisitionDate = datetime.fromtimestamp(raw_acq_date / 1000) if raw_acq_date else None # ESPN timestamps are milliseconds - self.proTeam = PRO_TEAM_MAP.get(json_parsing(data, 'proTeamId'), json_parsing(data, 'proTeamId')) - self.injuryStatus = json_parsing(data, 'injuryStatus') - self.status = json_parsing(data, 'status') + self.name = json_parsing(data, "fullName") + self.playerId = json_parsing(data, "id") + self.position = DEFAULT_POSITION_MAP.get( + json_parsing(data, "defaultPositionId"), + str(json_parsing(data, "defaultPositionId")), + ) + self.lineupSlot = POSITION_MAP.get(data.get("lineupSlotId"), "") + self.eligibleSlots = [ + POSITION_MAP.get(pos, pos) for pos in json_parsing(data, "eligibleSlots") + ] # if position isn't in position map, just use the position id number + self.acquisitionType = json_parsing(data, "acquisitionType") + raw_acq_date = json_parsing(data, "acquisitionDate") + self.acquisitionDate = ( + datetime.fromtimestamp(raw_acq_date / 1000) if raw_acq_date else None + ) # ESPN timestamps are milliseconds + self.proTeam = PRO_TEAM_MAP.get( + json_parsing(data, "proTeamId"), json_parsing(data, "proTeamId") + ) + self.injuryStatus = json_parsing(data, "injuryStatus") + self.status = json_parsing(data, "status") self.stats = {} # pool entry fields exist either nested under 'playerPoolEntry' (roster/free agent) # or at the top level (player_info card response) - pool_entry = data['playerPoolEntry'] if 'playerPoolEntry' in data else data - self.on_team_id = pool_entry.get('onTeamId') - self.keeper_value = pool_entry.get('keeperValue') - self.keeper_value_future = pool_entry.get('keeperValueFuture') - self.lineup_locked = pool_entry.get('lineupLocked', False) - self.roster_locked = pool_entry.get('rosterLocked', False) - self.trade_locked = pool_entry.get('tradeLocked', False) + pool_entry = data["playerPoolEntry"] if "playerPoolEntry" in data else data + self.on_team_id = pool_entry.get("onTeamId") + self.keeper_value = pool_entry.get("keeperValue") + self.keeper_value_future = pool_entry.get("keeperValueFuture") + self.lineup_locked = pool_entry.get("lineupLocked", False) + self.roster_locked = pool_entry.get("rosterLocked", False) + self.trade_locked = pool_entry.get("tradeLocked", False) - player = pool_entry.get('player') or data.get('player', {}) - self.injuryStatus = player.get('injuryStatus', self.injuryStatus) - self.injured = player.get('injured', False) - self.first_name = player.get('firstName', '') - self.last_name = player.get('lastName', '') - self.active = player.get('active', True) - self.droppable = player.get('droppable', True) - self.jersey = player.get('jersey') - self.laterality = player.get('laterality') - self.stance = player.get('stance') - raw_news_date = player.get('lastNewsDate') - self.last_news_date = datetime.fromtimestamp(raw_news_date / 1000) if raw_news_date else None # ESPN timestamps are milliseconds - self.season_outlook = player.get('seasonOutlook', '') + player = pool_entry.get("player") or data.get("player", {}) + self.injuryStatus = player.get("injuryStatus", self.injuryStatus) + self.injured = player.get("injured", False) + self.first_name = player.get("firstName", "") + self.last_name = player.get("lastName", "") + self.active = player.get("active", True) + self.droppable = player.get("droppable", True) + self.jersey = player.get("jersey") + self.laterality = player.get("laterality") + self.stance = player.get("stance") + raw_news_date = player.get("lastNewsDate") + self.last_news_date = ( + datetime.fromtimestamp(raw_news_date / 1000) if raw_news_date else None + ) # ESPN timestamps are milliseconds + self.season_outlook = player.get("seasonOutlook", "") - ownership = player.get('ownership', {}) - self.percent_owned = round(ownership.get('percentOwned', -1), 2) - self.percent_started = round(ownership.get('percentStarted', -1), 2) - self.percent_owned_change = ownership.get('percentChange') - self.adp = ownership.get('averageDraftPosition') - self.adp_change = ownership.get('averageDraftPositionPercentChange') - self.auction_value = ownership.get('auctionValueAverage') - self.auction_value_change = ownership.get('auctionValueAverageChange') + ownership = player.get("ownership", {}) + self.percent_owned = round(ownership.get("percentOwned", -1), 2) + self.percent_started = round(ownership.get("percentStarted", -1), 2) + self.percent_owned_change = ownership.get("percentChange") + self.adp = ownership.get("averageDraftPosition") + self.adp_change = ownership.get("averageDraftPositionPercentChange") + self.auction_value = ownership.get("auctionValueAverage") + self.auction_value_change = ownership.get("auctionValueAverageChange") self.draft_ranks = { - rank_type: {'rank': rank_data['rank'], 'auction_value': rank_data['auctionValue']} - for rank_type, rank_data in player.get('draftRanksByRankType', {}).items() + rank_type: { + "rank": rank_data["rank"], + "auction_value": rank_data["auctionValue"], + } + for rank_type, rank_data in player.get("draftRanksByRankType", {}).items() } # filter stats based on player eligibility: exclude pitcher-only stats if not eligible for pitcher slots, @@ -67,14 +90,14 @@ def __init__(self, data, year): # add available stats self.stats_splits = {label: {} for label in STAT_SPLIT_MAP.values()} - player_stats = player.get('stats', []) + player_stats = player.get("stats", []) for stats in player_stats: - stats_split_type = stats.get('statSplitTypeId') - if stats.get('seasonId') != year: + stats_split_type = stats.get("statSplitTypeId") + if stats.get("seasonId") != year: continue if stats_split_type not in STAT_SPLIT_MAP: continue # intentionally skip split types not in STAT_SPLIT_MAP (e.g. projected season) - stats_breakdown = stats.get('stats') or stats.get('appliedStats', {}) + stats_breakdown = stats.get("stats") or stats.get("appliedStats", {}) filtered_breakdown = {} for k, v in stats_breakdown.items(): stat_id = int(k) @@ -83,11 +106,17 @@ def __init__(self, data, year): if not is_eligible_batter and stat_id in BATTER_ONLY_STATS: continue # skip batter-only stats if not eligible for batter positions filtered_breakdown[k] = v - breakdown = {STATS_MAP.get(int(k), k): v for (k, v) in filtered_breakdown.items()} - points = round(stats.get('appliedTotal', 0), 2) - scoring_period = stats.get('scoringPeriodId') - stat_source = stats.get('statSourceId') - (points_type, breakdown_type) = ('points', 'breakdown') if stat_source == 0 else ('projected_points', 'projected_breakdown') + breakdown = { + STATS_MAP.get(int(k), k): v for (k, v) in filtered_breakdown.items() + } + points = round(stats.get("appliedTotal", 0), 2) + scoring_period = stats.get("scoringPeriodId") + stat_source = stats.get("statSourceId") + points_type, breakdown_type = ( + ("points", "breakdown") + if stat_source == 0 + else ("projected_points", "projected_breakdown") + ) # populate stats_splits for all split types split_label = STAT_SPLIT_MAP[stats_split_type] split_bucket = self.stats_splits[split_label] @@ -95,7 +124,10 @@ def __init__(self, data, year): split_bucket[scoring_period][points_type] = points split_bucket[scoring_period][breakdown_type] = breakdown else: - split_bucket[scoring_period] = {points_type: points, breakdown_type: breakdown} + split_bucket[scoring_period] = { + points_type: points, + breakdown_type: breakdown, + } # keep self.stats backwards-compatible: mirror season (0) and box_score (5) # splits into the legacy flat dict that existed before stats_splits if stats_split_type in (0, 5): @@ -103,10 +135,13 @@ def __init__(self, data, year): self.stats[scoring_period][points_type] = points self.stats[scoring_period][breakdown_type] = breakdown else: - self.stats[scoring_period] = {points_type: points, breakdown_type: breakdown} + self.stats[scoring_period] = { + points_type: points, + breakdown_type: breakdown, + } - self.total_points = self.stats.get(0, {}).get('points', 0) - self.projected_total_points = self.stats.get(0, {}).get('projected_points', 0) + self.total_points = self.stats.get(0, {}).get("points", 0) + self.projected_total_points = self.stats.get(0, {}).get("projected_points", 0) def __repr__(self): - return 'Player(%s)' % (self.name, ) + return "Player(%s)" % (self.name,) diff --git a/espn_api/baseball/settings.py b/espn_api/baseball/settings.py index 0804b059a..a448816db 100644 --- a/espn_api/baseball/settings.py +++ b/espn_api/baseball/settings.py @@ -5,7 +5,7 @@ class Settings(BaseSettings): def __init__(self, data): super().__init__(data) - lineup_slot_counts = data.get('rosterSettings', {}).get('lineupSlotCounts', {}) + lineup_slot_counts = data.get("rosterSettings", {}).get("lineupSlotCounts", {}) # slot IDs not in POSITION_MAP (e.g. bench, IR) are intentionally excluded self.position_slot_counts = { POSITION_MAP[int(slot_id)]: count diff --git a/espn_api/baseball/team.py b/espn_api/baseball/team.py index 25aaf582f..539b2e92e 100644 --- a/espn_api/baseball/team.py +++ b/espn_api/baseball/team.py @@ -2,69 +2,72 @@ from .matchup import Matchup from .constant import STATS_MAP + class Team(object): - '''Teams are part of the league''' + """Teams are part of the league""" + def __init__(self, data, roster, schedule, year, **kwargs): - self.team_id = data['id'] - self.team_abbrev = data['abbrev'] - self.team_name = data.get('name', 'Unknown') - if self.team_name == 'Unknown': - self.team_name = "%s %s" % (data.get('location', 'Unknown'), data.get('nickname', 'Unknown')) - self.division_id = data['divisionId'] - self.division_name = '' # set by caller - self.wins = data['record']['overall']['wins'] - self.losses = data['record']['overall']['losses'] - self.ties = data['record']['overall']['ties'] - self.points_for = data['record']['overall']['pointsFor'] - self.points_against = data['record']['overall']['pointsAgainst'] - self.streak_length = data['record']['overall']['streakLength'] - self.streak_type = data['record']['overall']['streakType'] - self.home_wins = data['record']['home']['wins'] - self.home_losses = data['record']['home']['losses'] - self.home_ties = data['record']['home']['ties'] - self.away_wins = data['record']['away']['wins'] - self.away_losses = data['record']['away']['losses'] - self.away_ties = data['record']['away']['ties'] - self.division_wins = data['record']['division']['wins'] - self.division_losses = data['record']['division']['losses'] - self.division_ties = data['record']['division']['ties'] - self.current_projected_rank = data.get('currentProjectedRank') - self.waiver_rank = data.get('waiverRank') - self.points = data.get('points', 0) - self.logo_url = '' - self.standing = data['playoffSeed'] - self.final_standing = data.get('rankFinal') or data.get('rankCalculatedFinal') + self.team_id = data["id"] + self.team_abbrev = data["abbrev"] + self.team_name = data.get("name", "Unknown") + if self.team_name == "Unknown": + self.team_name = "%s %s" % ( + data.get("location", "Unknown"), + data.get("nickname", "Unknown"), + ) + self.division_id = data["divisionId"] + self.division_name = "" # set by caller + self.wins = data["record"]["overall"]["wins"] + self.losses = data["record"]["overall"]["losses"] + self.ties = data["record"]["overall"]["ties"] + self.points_for = data["record"]["overall"]["pointsFor"] + self.points_against = data["record"]["overall"]["pointsAgainst"] + self.streak_length = data["record"]["overall"]["streakLength"] + self.streak_type = data["record"]["overall"]["streakType"] + self.home_wins = data["record"]["home"]["wins"] + self.home_losses = data["record"]["home"]["losses"] + self.home_ties = data["record"]["home"]["ties"] + self.away_wins = data["record"]["away"]["wins"] + self.away_losses = data["record"]["away"]["losses"] + self.away_ties = data["record"]["away"]["ties"] + self.division_wins = data["record"]["division"]["wins"] + self.division_losses = data["record"]["division"]["losses"] + self.division_ties = data["record"]["division"]["ties"] + self.current_projected_rank = data.get("currentProjectedRank") + self.waiver_rank = data.get("waiverRank") + self.points = data.get("points", 0) + self.logo_url = "" + self.standing = data["playoffSeed"] + self.final_standing = data.get("rankFinal") or data.get("rankCalculatedFinal") self.roster = [] self.schedule = [] - if 'logo' in data: - self.logo_url = data['logo'] - + if "logo" in data: + self.logo_url = data["logo"] + self._fetch_roster(roster, year) self._fetch_schedule(schedule) - self.owners = kwargs.get('owners', []) - + self.owners = kwargs.get("owners", []) + def __repr__(self): - return f'Team({self.team_name})' - + return f"Team({self.team_name})" def _fetch_roster(self, data, year): - '''Fetch teams roster''' + """Fetch teams roster""" self.roster.clear() - roster = data['entries'] + roster = data["entries"] for player in roster: self.roster.append(Player(player, year)) - def _fetch_schedule(self, data): - '''Fetch schedule and scores for team''' + """Fetch schedule and scores for team""" for match in data: - if 'away' in match.keys(): - if match['away']['teamId'] == self.team_id: + if "away" in match.keys(): + if match["away"]["teamId"] == self.team_id: new_match = Matchup(match) - setattr(new_match, 'away_team', self) + setattr(new_match, "away_team", self) self.schedule.append(new_match) - elif match['home']['teamId'] == self.team_id: + elif match["home"]["teamId"] == self.team_id: new_match = Matchup(match) - setattr(new_match, 'home_team', self) + setattr(new_match, "home_team", self) self.schedule.append(new_match) diff --git a/espn_api/baseball/transaction.py b/espn_api/baseball/transaction.py index 608be08d0..84bf337b4 100644 --- a/espn_api/baseball/transaction.py +++ b/espn_api/baseball/transaction.py @@ -1,41 +1,48 @@ from datetime import datetime from typing import Any, Callable, Dict + class Transaction(object): - def __init__(self, data: dict, player_map: Dict[int, str], get_team_data: Callable[[int], Any]): - self.team_id = data['teamId'] + def __init__( + self, + data: dict, + player_map: Dict[int, str], + get_team_data: Callable[[int], Any], + ): + self.team_id = data["teamId"] self.team = get_team_data(self.team_id) - self.type = data['type'] - self.status = data.get('status') - self.scoring_period = data['scoringPeriodId'] - raw_date = data.get('processDate') or data.get('proposedDate') + self.type = data["type"] + self.status = data.get("status") + self.scoring_period = data["scoringPeriodId"] + raw_date = data.get("processDate") or data.get("proposedDate") self.date = datetime.fromtimestamp(raw_date / 1000) if raw_date else None - self.bid_amount = data.get('bidAmount') - self.is_pending = data.get('isPending', self.status == 'PENDING') - self.rating = data.get('rating') - self.execution_type = data.get('executionType') - self.related_transaction_id = data.get('relatedTransactionId') - self.comment = data.get('comment') - self.member_id = data.get('memberId') + self.bid_amount = data.get("bidAmount") + self.is_pending = data.get("isPending", self.status == "PENDING") + self.rating = data.get("rating") + self.execution_type = data.get("executionType") + self.related_transaction_id = data.get("relatedTransactionId") + self.comment = data.get("comment") + self.member_id = data.get("memberId") self.items = [] - for item in data.get('items', []): + for item in data.get("items", []): self.items.append(TransactionItem(item, player_map)) def __repr__(self): - items = ', '.join([str(item) for item in self.items]) - team_name = self.team.team_name if self.team else f'Team({self.team_id})' - return f'Transaction({team_name} {self.type} {items})' + items = ", ".join([str(item) for item in self.items]) + team_name = self.team.team_name if self.team else f"Team({self.team_id})" + return f"Transaction({team_name} {self.type} {items})" + class TransactionItem(object): def __init__(self, data, player_map): - self.type = data['type'] - self.player_name = player_map.get(data['playerId'], 'Unknown') - self.from_team_id = data.get('fromTeamId') - self.to_team_id = data.get('toTeamId') - self.from_lineup_slot_id = data.get('fromLineupSlotId') - self.to_lineup_slot_id = data.get('toLineupSlotId') - self.is_keeper = data.get('isKeeper', False) - self.overall_pick_number = data.get('overallPickNumber') + self.type = data["type"] + self.player_name = player_map.get(data["playerId"], "Unknown") + self.from_team_id = data.get("fromTeamId") + self.to_team_id = data.get("toTeamId") + self.from_lineup_slot_id = data.get("fromLineupSlotId") + self.to_lineup_slot_id = data.get("toLineupSlotId") + self.is_keeper = data.get("isKeeper", False) + self.overall_pick_number = data.get("overallPickNumber") def __repr__(self): - return f'{self.type} {self.player_name}' + return f"{self.type} {self.player_name}" diff --git a/espn_api/baseball/utils.py b/espn_api/baseball/utils.py index bd5690cfc..272bc873c 100644 --- a/espn_api/baseball/utils.py +++ b/espn_api/baseball/utils.py @@ -1,5 +1,6 @@ # Helper functions for json parsing and power rankings + def json_parsing(obj, key): """Recursively pull values of specified key from nested JSON.""" arr = [] @@ -8,7 +9,9 @@ def extract(obj, arr, key): """Return all matching values in an object.""" if isinstance(obj, dict): for k, v in obj.items(): - if isinstance(v, (dict)) or (isinstance(v, (list)) and v and isinstance(v[0], (list, dict))): + if isinstance(v, (dict)) or ( + isinstance(v, (list)) and v and isinstance(v[0], (list, dict)) + ): extract(v, arr, key) elif k == key: arr.append(v) diff --git a/espn_api/basketball/__init__.py b/espn_api/basketball/__init__.py index f36640171..b9baae89b 100644 --- a/espn_api/basketball/__init__.py +++ b/espn_api/basketball/__init__.py @@ -1,11 +1,11 @@ -__all__ = ['League', - 'Team', - 'Player', - 'Matchup', - ] +__all__ = [ + "League", + "Team", + "Player", + "Matchup", +] from .league import League from .team import Team from .player import Player from .matchup import Matchup - diff --git a/espn_api/basketball/activity.py b/espn_api/basketball/activity.py index 5f0877a84..4679ea8e4 100644 --- a/espn_api/basketball/activity.py +++ b/espn_api/basketball/activity.py @@ -1,42 +1,41 @@ from .constant import ACTIVITY_MAP, POSITION_MAP + class Activity(object): def __init__(self, data, player_map, get_team_data, include_moved=False): - self.actions = [] # List of tuples (Team, action, player) - self.date = data['date'] - for msg in data['messages']: - team = '' - action = 'UNKNOWN' - player = '' - position = '' - msg_id = msg['messageTypeId'] + self.actions = [] # List of tuples (Team, action, player) + self.date = data["date"] + for msg in data["messages"]: + team = "" + action = "UNKNOWN" + player = "" + position = "" + msg_id = msg["messageTypeId"] if msg_id == 244: - team = get_team_data(msg['from']) + team = get_team_data(msg["from"]) elif msg_id == 239: - team = get_team_data(msg['for']) - elif msg_id == 188 and include_moved and msg['to'] in POSITION_MAP: - position = POSITION_MAP[msg['to']] + team = get_team_data(msg["for"]) + elif msg_id == 188 and include_moved and msg["to"] in POSITION_MAP: + position = POSITION_MAP[msg["to"]] else: - team = get_team_data(msg['to']) + team = get_team_data(msg["to"]) if msg_id in ACTIVITY_MAP: if include_moved: action = ACTIVITY_MAP[msg_id] elif msg_id != 188: action = ACTIVITY_MAP[msg_id] - if msg['targetId'] in player_map: - player = player_map[msg['targetId']] - if action != 'UNKNOWN': + if msg["targetId"] in player_map: + player = player_map[msg["targetId"]] + if action != "UNKNOWN": self.actions.append((team, action, player, position)) - + def __repr__(self): def format_action(tup): - return '(%s)' % ','.join(str(x) for x in tup if x) + return "(%s)" % ",".join(str(x) for x in tup if x) + if self.actions: - return 'Activity(' + ' '.join(format_action(tup) for tup in self.actions) + ')' + return ( + "Activity(" + " ".join(format_action(tup) for tup in self.actions) + ")" + ) else: - return '' - - - - - + return "" diff --git a/espn_api/basketball/box_player.py b/espn_api/basketball/box_player.py index 400ec3edc..f8ef6e8c5 100644 --- a/espn_api/basketball/box_player.py +++ b/espn_api/basketball/box_player.py @@ -2,34 +2,49 @@ from .player import Player from datetime import datetime, timedelta + class BoxPlayer(Player): - '''player with extra data from a matchup''' + """player with extra data from a matchup""" + def __init__(self, data, pro_schedule, year, scoring_period): super(BoxPlayer, self).__init__(data, year, pro_schedule) - self.slot_position = 'FA' - self.pro_opponent = "None" # professional team playing against - self.game_played = 100 # 0-100 for percent of game played + self.slot_position = "FA" + self.pro_opponent = "None" # professional team playing against + self.game_played = 100 # 0-100 for percent of game played self.points = 0 self.points_breakdown = {} - if 'lineupSlotId' in data: - self.slot_position = POSITION_MAP[data['lineupSlotId']] + if "lineupSlotId" in data: + self.slot_position = POSITION_MAP[data["lineupSlotId"]] - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] - pro_id = player['proTeamId'] + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) + pro_id = player["proTeamId"] if pro_id in pro_schedule and str(scoring_period) in pro_schedule[pro_id]: game = pro_schedule[pro_id][str(scoring_period)][0] - opp_id = game['awayProTeamId'] if game['awayProTeamId'] != player['proTeamId'] else game['homeProTeamId'] - self.game_played = 100 if datetime.now() > datetime.fromtimestamp(game['date']/1000.0) + timedelta(hours=3) else 0 + opp_id = ( + game["awayProTeamId"] + if game["awayProTeamId"] != player["proTeamId"] + else game["homeProTeamId"] + ) + self.game_played = ( + 100 + if datetime.now() + > datetime.fromtimestamp(game["date"] / 1000.0) + timedelta(hours=3) + else 0 + ) self.pro_opponent = PRO_TEAM_MAP[opp_id] - - player_stats = player.get('stats', []) + + player_stats = player.get("stats", []) for stats in player_stats: - stats_breakdown = stats.get('appliedStats') or stats.get('stats', {}) - breakdown = {STATS_MAP.get(k, k):v for (k,v) in stats_breakdown.items()} - points = round(stats.get('appliedTotal', 0), 2) + stats_breakdown = stats.get("appliedStats") or stats.get("stats", {}) + breakdown = {STATS_MAP.get(k, k): v for (k, v) in stats_breakdown.items()} + points = round(stats.get("appliedTotal", 0), 2) self.points = points self.points_breakdown = breakdown def __repr__(self): - return f'Player({self.name}, points:{self.points})' + return f"Player({self.name}, points:{self.points})" diff --git a/espn_api/basketball/box_score.py b/espn_api/basketball/box_score.py index b8d8171e6..40a9f788a 100644 --- a/espn_api/basketball/box_score.py +++ b/espn_api/basketball/box_score.py @@ -3,80 +3,114 @@ from .box_player import BoxPlayer + class BoxScore(ABC): - ''' ''' - def __init__(self, data, scoring_period): - self.winner = data.get('winner', 'UNDECIDED') - self.home_team = data.get('home', {}).get('teamId', 0) - self.away_team = data.get('away', {}).get('teamId', 0) - self.scoring_period = scoring_period - - def __repr__(self): - away_team = self.away_team or "BYE" - home_team = self.home_team or "BYE" - return f'Box Score({away_team} at {home_team})' - - def _get_player_lineup(self, team, data, pro_schedule, by_matchup, year): - if team not in data: - return [] - - roster_key = 'rosterForMatchupPeriod' if by_matchup else 'rosterForCurrentScoringPeriod' - roster = data[team].get(roster_key, {}) - lineup = [BoxPlayer(player, pro_schedule, year, self.scoring_period) for player in roster.get('entries', [])] - - return lineup + """ """ -class H2HPointsBoxScore(BoxScore): - def __init__(self, data, pro_schedule, by_matchup, year, scoring_period = 0): - super().__init__(data, scoring_period) + def __init__(self, data, scoring_period): + self.winner = data.get("winner", "UNDECIDED") + self.home_team = data.get("home", {}).get("teamId", 0) + self.away_team = data.get("away", {}).get("teamId", 0) + self.scoring_period = scoring_period - (self.home_score, self.home_projected, self.home_lineup) = self._get_team_data('home', data, pro_schedule, by_matchup, year) + def __repr__(self): + away_team = self.away_team or "BYE" + home_team = self.home_team or "BYE" + return f"Box Score({away_team} at {home_team})" - (self.away_score, self.away_projected, self.away_lineup) = self._get_team_data('away', data, pro_schedule, by_matchup, year) + def _get_player_lineup(self, team, data, pro_schedule, by_matchup, year): + if team not in data: + return [] - def _get_team_data(self, team, data, pro_schedule, by_matchup, year): - if team not in data: - return (0, -1, []) - - team_projected = -1 - roster_key = 'rosterForMatchupPeriod' if by_matchup else 'rosterForCurrentScoringPeriod' - team_roster = data[team].get(roster_key, {}) - if 'totalPointsLive' in data[team] and by_matchup: - team_score = round(data[team]['totalPointsLive'], 2) - team_projected = round(data[team].get('totalProjectedPointsLive', -1), 2) - else: - team_score = round(team_roster.get('appliedStatTotal', 0), 2) - lineup = self._get_player_lineup(team, data, pro_schedule, by_matchup, year) + roster_key = ( + "rosterForMatchupPeriod" if by_matchup else "rosterForCurrentScoringPeriod" + ) + roster = data[team].get(roster_key, {}) + lineup = [ + BoxPlayer(player, pro_schedule, year, self.scoring_period) + for player in roster.get("entries", []) + ] - return (team_score, team_projected, lineup) + return lineup -class H2HCategoryBoxScore(BoxScore): - def __init__(self, data, pro_schedule, by_matchup, year, scoring_period = 0): - super().__init__(data, scoring_period) - (self.home_wins, self.home_ties, self.home_losses, self.home_stats, self.home_lineup) = self._get_team_data('home', data, pro_schedule, by_matchup, year) +class H2HPointsBoxScore(BoxScore): + def __init__(self, data, pro_schedule, by_matchup, year, scoring_period=0): + super().__init__(data, scoring_period) + + self.home_score, self.home_projected, self.home_lineup = self._get_team_data( + "home", data, pro_schedule, by_matchup, year + ) - (self.away_wins, self.away_ties, self.away_losses, self.away_stats, self.away_lineup) = self._get_team_data('away', data, pro_schedule, by_matchup, year) - - def _get_team_data(self, team, data, pro_schedule, by_matchup, year): - if team not in data: - return (0, 0, 0, {}, []) - cumulative_score = data[team].get('cumulativeScore', {}) - team_wins = cumulative_score.get('wins', 0) - team_ties = cumulative_score.get('ties', 0) - team_losses = cumulative_score.get('losses', 0) + self.away_score, self.away_projected, self.away_lineup = self._get_team_data( + "away", data, pro_schedule, by_matchup, year + ) - team_stats = {} - for stat_key, stat_dict in cumulative_score.get('scoreByStat', {}).items(): - team_stats[STATS_MAP.get(stat_key, stat_key)] = { - 'value': stat_dict['score'], - 'result': stat_dict['result'] - } + def _get_team_data(self, team, data, pro_schedule, by_matchup, year): + if team not in data: + return (0, -1, []) - lineup = self._get_player_lineup(team, data, pro_schedule, by_matchup, year) + team_projected = -1 + roster_key = ( + "rosterForMatchupPeriod" if by_matchup else "rosterForCurrentScoringPeriod" + ) + team_roster = data[team].get(roster_key, {}) + if "totalPointsLive" in data[team] and by_matchup: + team_score = round(data[team]["totalPointsLive"], 2) + team_projected = round(data[team].get("totalProjectedPointsLive", -1), 2) + else: + team_score = round(team_roster.get("appliedStatTotal", 0), 2) + lineup = self._get_player_lineup(team, data, pro_schedule, by_matchup, year) + + return (team_score, team_projected, lineup) + + +class H2HCategoryBoxScore(BoxScore): + def __init__(self, data, pro_schedule, by_matchup, year, scoring_period=0): + super().__init__(data, scoring_period) + + ( + self.home_wins, + self.home_ties, + self.home_losses, + self.home_stats, + self.home_lineup, + ) = self._get_team_data("home", data, pro_schedule, by_matchup, year) + + ( + self.away_wins, + self.away_ties, + self.away_losses, + self.away_stats, + self.away_lineup, + ) = self._get_team_data("away", data, pro_schedule, by_matchup, year) + + def _get_team_data(self, team, data, pro_schedule, by_matchup, year): + if team not in data: + return (0, 0, 0, {}, []) + cumulative_score = data[team].get("cumulativeScore", {}) + team_wins = cumulative_score.get("wins", 0) + team_ties = cumulative_score.get("ties", 0) + team_losses = cumulative_score.get("losses", 0) + + team_stats = {} + for stat_key, stat_dict in cumulative_score.get("scoreByStat", {}).items(): + team_stats[STATS_MAP.get(stat_key, stat_key)] = { + "value": stat_dict["score"], + "result": stat_dict["result"], + } + + lineup = self._get_player_lineup(team, data, pro_schedule, by_matchup, year) + + return (team_wins, team_ties, team_losses, team_stats, lineup) - return (team_wins, team_ties, team_losses, team_stats, lineup) # helper function to get correct box score class -ScoringType = {'H2H_POINTS': H2HPointsBoxScore, 'H2H_CATEGORY': H2HCategoryBoxScore, 'H2H_MOST_CATEGORIES': H2HCategoryBoxScore} -get_box_scoring_type_class = lambda scoring_type: ScoringType.get(scoring_type, H2HPointsBoxScore) +ScoringType = { + "H2H_POINTS": H2HPointsBoxScore, + "H2H_CATEGORY": H2HCategoryBoxScore, + "H2H_MOST_CATEGORIES": H2HCategoryBoxScore, +} +get_box_scoring_type_class = lambda scoring_type: ScoringType.get( + scoring_type, H2HPointsBoxScore +) diff --git a/espn_api/basketball/constant.py b/espn_api/basketball/constant.py index d33672ee6..0c7f8126d 100644 --- a/espn_api/basketball/constant.py +++ b/espn_api/basketball/constant.py @@ -1,166 +1,166 @@ POSITION_MAP = { - 0: 'PG', - 1: 'SG', - 2: 'SF', - 3: 'PF', - 4: 'C', - 5: 'G', - 6: 'F', - 7: 'SG/SF', - 8: 'G/F', - 9: 'PF/C', - 10: 'F/C', - 11: 'UT', - 12: 'BE', - 13: 'IR', - 14: '', - 15: 'Rookie', + 0: "PG", + 1: "SG", + 2: "SF", + 3: "PF", + 4: "C", + 5: "G", + 6: "F", + 7: "SG/SF", + 8: "G/F", + 9: "PF/C", + 10: "F/C", + 11: "UT", + 12: "BE", + 13: "IR", + 14: "", + 15: "Rookie", # reverse - 'PG': 0, - 'SG': 1, - 'SF': 2, - 'PF': 3, - 'C': 4, - 'G': 5, - 'F': 6, - 'SG/SF': 7, - 'G/F': 8, - 'PF/C': 9, - 'F/C': 10, - 'UT': 11, - 'BE': 12, - 'IR': 13, - 'Rookie': 15, + "PG": 0, + "SG": 1, + "SF": 2, + "PF": 3, + "C": 4, + "G": 5, + "F": 6, + "SG/SF": 7, + "G/F": 8, + "PF/C": 9, + "F/C": 10, + "UT": 11, + "BE": 12, + "IR": 13, + "Rookie": 15, } PRO_TEAM_MAP = { - 0: 'FA', - 1: 'ATL', - 2: 'BOS', - 3: 'NOP', - 4: 'CHI', - 5: 'CLE', - 6: 'DAL', - 7: 'DEN', - 8: 'DET', - 9: 'GSW', - 10: 'HOU', - 11: 'IND', - 12: 'LAC', - 13: 'LAL', - 14: 'MIA', - 15: 'MIL', - 16: 'MIN', - 17: 'BKN', - 18: 'NYK', - 19: 'ORL', - 20: 'PHL', - 21: 'PHO', - 22: 'POR', - 23: 'SAC', - 24: 'SAS', - 25: 'OKC', - 26: 'UTA', - 27: 'WAS', - 28: 'TOR', - 29: 'MEM', - 30: 'CHA', + 0: "FA", + 1: "ATL", + 2: "BOS", + 3: "NOP", + 4: "CHI", + 5: "CLE", + 6: "DAL", + 7: "DEN", + 8: "DET", + 9: "GSW", + 10: "HOU", + 11: "IND", + 12: "LAC", + 13: "LAL", + 14: "MIA", + 15: "MIL", + 16: "MIN", + 17: "BKN", + 18: "NYK", + 19: "ORL", + 20: "PHL", + 21: "PHO", + 22: "POR", + 23: "SAC", + 24: "SAS", + 25: "OKC", + 26: "UTA", + 27: "WAS", + 28: "TOR", + 29: "MEM", + 30: "CHA", } STATS_MAP = { - '0': 'PTS', - '1': 'BLK', - '2': 'STL', - '3': 'AST', - '4': 'OREB', - '5': 'DREB', - '6': 'REB', - '7': 'EJ', - '8': 'FF', - '9': 'PF', - '10': 'TF', - '11': 'TO', - '12': 'DQ', - '13': 'FGM', - '14': 'FGA', - '15': 'FTM', - '16': 'FTA', - '17': '3PM', - '18': '3PA', - '19': 'FG%', - '20': 'FT%', - '21': '3PT%', - '22': 'AFG%', - '23': 'FGMI', - '24': 'FTMI', - '25': '3PMI', - '26': 'APG', - '27': 'BPG', - '28': 'MPG', - '29': 'PPG', - '30': 'RPG', - '31': 'SPG', - '32': 'TOPG', - '33': '3PG', - '34': 'PPM', - '35': 'A/TO', - '36': 'STR', - '37': 'DD', - '38': 'TD', - '39': 'QD', - '40': 'MIN', - '41': 'GS', - '42': 'GP', - '43': 'TW', - '44': 'FTR', - '45': '45', + "0": "PTS", + "1": "BLK", + "2": "STL", + "3": "AST", + "4": "OREB", + "5": "DREB", + "6": "REB", + "7": "EJ", + "8": "FF", + "9": "PF", + "10": "TF", + "11": "TO", + "12": "DQ", + "13": "FGM", + "14": "FGA", + "15": "FTM", + "16": "FTA", + "17": "3PM", + "18": "3PA", + "19": "FG%", + "20": "FT%", + "21": "3PT%", + "22": "AFG%", + "23": "FGMI", + "24": "FTMI", + "25": "3PMI", + "26": "APG", + "27": "BPG", + "28": "MPG", + "29": "PPG", + "30": "RPG", + "31": "SPG", + "32": "TOPG", + "33": "3PG", + "34": "PPM", + "35": "A/TO", + "36": "STR", + "37": "DD", + "38": "TD", + "39": "QD", + "40": "MIN", + "41": "GS", + "42": "GP", + "43": "TW", + "44": "FTR", + "45": "45", } STAT_ID_MAP = { - '00': 'total', - '10': 'projected', - '01': 'last_7', - '02': 'last_15', - '03': 'last_30', + "00": "total", + "10": "projected", + "01": "last_7", + "02": "last_15", + "03": "last_30", } ACTIVITY_MAP = { - 178: 'FA ADDED', - 180: 'WAIVER ADDED', - 179: 'DROPPED', - 181: 'DROPPED', - 188: 'MOVED', - 239: 'DROPPED', - 244: 'TRADED', - 'FA': 178, - 'WAIVER': 180, - 'TRADED': 244, + 178: "FA ADDED", + 180: "WAIVER ADDED", + 179: "DROPPED", + 181: "DROPPED", + 188: "MOVED", + 239: "DROPPED", + 244: "TRADED", + "FA": 178, + "WAIVER": 180, + "TRADED": 244, } TRANSACTION_TYPES = { - 'DRAFT', - 'TRADE_ACCEPT', - 'WAIVER', - 'TRADE_VETO', - 'FUTURE_ROSTER', - 'ROSTER', - 'RETRO_ROSTER', - 'TRADE_PROPOSAL', - 'TRADE_UPHOLD', - 'FREEAGENT', - 'TRADE_DECLINE', - 'WAIVER_ERROR', - 'TRADE_ERROR' + "DRAFT", + "TRADE_ACCEPT", + "WAIVER", + "TRADE_VETO", + "FUTURE_ROSTER", + "ROSTER", + "RETRO_ROSTER", + "TRADE_PROPOSAL", + "TRADE_UPHOLD", + "FREEAGENT", + "TRADE_DECLINE", + "WAIVER_ERROR", + "TRADE_ERROR", } NINE_CAT_STATS = { - '3PM', - 'AST', - 'BLK', - 'FG%', - 'FT%', - 'PTS', - 'REB', - 'STL', - 'TO', + "3PM", + "AST", + "BLK", + "FG%", + "FT%", + "PTS", + "REB", + "STL", + "TO", } diff --git a/espn_api/basketball/league.py b/espn_api/basketball/league.py index 2784aa959..d048c0510 100644 --- a/espn_api/basketball/league.py +++ b/espn_api/basketball/league.py @@ -10,11 +10,28 @@ from .transaction import Transaction from .constant import POSITION_MAP, ACTIVITY_MAP, TRANSACTION_TYPES + class League(BaseLeague): teams: List[Team] - '''Creates a League instance for Public/Private ESPN league''' - def __init__(self, league_id: int, year: int, espn_s2=None, swid=None, fetch_league=True, debug=False): - super().__init__(league_id=league_id, year=year, sport='nba', espn_s2=espn_s2, swid=swid, debug=debug) + """Creates a League instance for Public/Private ESPN league""" + + def __init__( + self, + league_id: int, + year: int, + espn_s2=None, + swid=None, + fetch_league=True, + debug=False, + ): + super().__init__( + league_id=league_id, + year=year, + sport="nba", + espn_s2=espn_s2, + swid=swid, + debug=debug, + ) if fetch_league: self.fetch_league() @@ -30,29 +47,32 @@ def _fetch_league(self): data = super()._fetch_league() self._fetch_players() - self._map_matchup_ids(data['schedule']) - return(data) + self._map_matchup_ids(data["schedule"]) + return data def _map_matchup_ids(self, schedule): self.matchup_ids = {} for match in schedule: - matchup_period = match.get('matchupPeriodId') - scoring_periods = match.get('home', {}).get('pointsByScoringPeriod', {}).keys() + matchup_period = match.get("matchupPeriodId") + scoring_periods = ( + match.get("home", {}).get("pointsByScoringPeriod", {}).keys() + ) if len(scoring_periods) > 0: if matchup_period not in self.matchup_ids: self.matchup_ids[matchup_period] = sorted(scoring_periods) else: - self.matchup_ids[matchup_period] = sorted(set(self.matchup_ids[matchup_period] + list(scoring_periods))) - + self.matchup_ids[matchup_period] = sorted( + set(self.matchup_ids[matchup_period] + list(scoring_periods)) + ) def _fetch_teams(self, data): - '''Fetch teams in league''' + """Fetch teams in league""" self.pro_schedule = self._get_all_pro_schedule() super()._fetch_teams(data, TeamClass=Team, pro_schedule=self.pro_schedule) # replace opponentIds in schedule with team instances for team in self.teams: - team.division_name = self.settings.division_map.get(team.division_id, '') + team.division_name = self.settings.division_map.get(team.division_id, "") for week, matchup in enumerate(team.schedule): for opponent in self.teams: if matchup.away_team == opponent.team_id: @@ -61,20 +81,28 @@ def _fetch_teams(self, data): matchup.home_team = opponent def standings(self) -> List[Team]: - standings = sorted(self.teams, key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, reverse=False) + standings = sorted( + self.teams, + key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, + reverse=False, + ) return standings def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: - '''Returns list of matchups for a given matchup period''' + """Returns list of matchups for a given matchup period""" if not matchupPeriod: - matchupPeriod=self.currentMatchupPeriod + matchupPeriod = self.currentMatchupPeriod params = { - 'view': 'mMatchup', + "view": "mMatchup", } data = self.espn_request.league_get(params=params) - schedule = data['schedule'] - matchups = [Matchup(matchup) for matchup in schedule if matchup['matchupPeriodId'] == matchupPeriod] + schedule = data["schedule"] + matchups = [ + Matchup(matchup) + for matchup in schedule + if matchup["matchupPeriodId"] == matchupPeriod + ] for team in self.teams: for matchup in matchups: @@ -85,53 +113,83 @@ def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: return matchups - def recent_activity(self, size: int = 25, msg_type: str = None, offset: int = 0, include_moved=False) -> List[Activity]: - '''Returns a list of recent league activities (Add, Drop, Trade)''' + def recent_activity( + self, size: int = 25, msg_type: str = None, offset: int = 0, include_moved=False + ) -> List[Activity]: + """Returns a list of recent league activities (Add, Drop, Trade)""" if self.year < 2019: - raise Exception('Cant use recent activity before 2019') + raise Exception("Cant use recent activity before 2019") - msg_types = [178,180,179,239,181,244,188] + msg_types = [178, 180, 179, 239, 181, 244, 188] if msg_type in ACTIVITY_MAP: msg_types = [ACTIVITY_MAP[msg_type]] - params = { - 'view': 'kona_league_communication' + params = {"view": "kona_league_communication"} + + filters = { + "topics": { + "filterType": {"value": ["ACTIVITY_TRANSACTIONS"]}, + "limit": size, + "limitPerMessageSet": {"value": 25}, + "offset": offset, + "sortMessageDate": {"sortPriority": 1, "sortAsc": False}, + "sortFor": {"sortPriority": 2, "sortAsc": False}, + "filterIncludeMessageTypeIds": {"value": msg_types}, + } } - - filters = {"topics":{"filterType":{"value":["ACTIVITY_TRANSACTIONS"]},"limit":size,"limitPerMessageSet":{"value":25},"offset":offset,"sortMessageDate":{"sortPriority":1,"sortAsc":False},"sortFor":{"sortPriority":2,"sortAsc":False},"filterIncludeMessageTypeIds":{"value":msg_types}}} - headers = {'x-fantasy-filter': json.dumps(filters)} - data = self.espn_request.league_get(extend='/communication/', params=params, headers=headers) - data = data['topics'] - activity = [Activity(topic, self.player_map, self.get_team_data, include_moved=include_moved) for topic in data] + headers = {"x-fantasy-filter": json.dumps(filters)} + data = self.espn_request.league_get( + extend="/communication/", params=params, headers=headers + ) + data = data["topics"] + activity = [ + Activity( + topic, self.player_map, self.get_team_data, include_moved=include_moved + ) + for topic in data + ] return activity - def transactions(self, scoring_period: int = None, types: Set[str] = {"FREEAGENT","WAIVER","WAIVER_ERROR"}) -> List[Transaction]: - '''Returns a list of recent transactions''' + def transactions( + self, + scoring_period: int = None, + types: Set[str] = {"FREEAGENT", "WAIVER", "WAIVER_ERROR"}, + ) -> List[Transaction]: + """Returns a list of recent transactions""" if not scoring_period: scoring_period = self.scoringPeriodId if types > TRANSACTION_TYPES: - raise Exception('Invalid transaction type') + raise Exception("Invalid transaction type") params = { - 'view': 'mTransactions2', - 'scoringPeriodId': scoring_period, + "view": "mTransactions2", + "scoringPeriodId": scoring_period, } - filters = {"transactions":{"filterType":{"value":list(types)}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = {"transactions": {"filterType": {"value": list(types)}}} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - transactions = data['transactions'] - - return [Transaction(transaction, self.player_map, self.get_team_data) for transaction in transactions] - - def free_agents(self, week: int=None, size: int=50, position: str=None, position_id: int=None) -> List[Player]: - '''Returns a List of Free Agents for a Given Week\n - Should only be used with most recent season''' + transactions = data["transactions"] + + return [ + Transaction(transaction, self.player_map, self.get_team_data) + for transaction in transactions + ] + + def free_agents( + self, + week: int = None, + size: int = 50, + position: str = None, + position_id: int = None, + ) -> List[Player]: + """Returns a List of Free Agents for a Given Week\n + Should only be used with most recent season""" if self.year < 2019: - raise Exception('Cant use free agents before 2019') + raise Exception("Cant use free agents before 2019") if not week: week = self.current_week @@ -141,23 +199,39 @@ def free_agents(self, week: int=None, size: int=50, position: str=None, position if position_id: slot_filter.append(position_id) - params = { - 'view': 'kona_player_info', - 'scoringPeriodId': week, + "view": "kona_player_info", + "scoringPeriodId": week, + } + filters = { + "players": { + "filterStatus": {"value": ["FREEAGENT", "WAIVERS"]}, + "filterSlotIds": {"value": slot_filter}, + "limit": size, + "sortPercOwned": {"sortPriority": 1, "sortAsc": False}, + "sortDraftRanks": { + "sortPriority": 100, + "sortAsc": True, + "value": "STANDARD", + }, + } } - filters = {"players":{"filterStatus":{"value":["FREEAGENT","WAIVERS"]},"filterSlotIds":{"value":slot_filter},"limit":size,"sortPercOwned":{"sortPriority":1,"sortAsc":False},"sortDraftRanks":{"sortPriority":100,"sortAsc":True,"value":"STANDARD"}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - players = data['players'] + players = data["players"] return [Player(player, self.year) for player in players] - def box_scores(self, matchup_period: int = None, scoring_period: int = None, matchup_total: bool = True) -> List[BoxScore]: - '''Returns list of box score for a given matchup or scoring period''' + def box_scores( + self, + matchup_period: int = None, + scoring_period: int = None, + matchup_total: bool = True, + ) -> List[BoxScore]: + """Returns list of box score for a given matchup or scoring period""" if self.year < 2019: - raise Exception('Cant use box score before 2019') + raise Exception("Cant use box score before 2019") matchup_id = self.currentMatchupPeriod scoring_id = self.current_week @@ -166,7 +240,11 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat scoring_id = scoring_period elif matchup_period and matchup_period < matchup_id: matchup_id = matchup_period - scoring_id = self.matchup_ids[matchup_period][-1] if matchup_period in self.matchup_ids else 1 + scoring_id = ( + self.matchup_ids[matchup_period][-1] + if matchup_period in self.matchup_ids + else 1 + ) elif scoring_period and scoring_period <= scoring_id: scoring_id = scoring_period for matchup in self.matchup_ids.keys(): @@ -175,16 +253,21 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat break params = { - 'view': ['mMatchupScore', 'mScoreboard'], - 'scoringPeriodId': scoring_id + "view": ["mMatchupScore", "mScoreboard"], + "scoringPeriodId": scoring_id, } - filters = {"schedule":{"filterMatchupPeriodIds":{"value":[matchup_id]}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = {"schedule": {"filterMatchupPeriodIds": {"value": [matchup_id]}}} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - schedule = data['schedule'] - box_data = [self.BoxScoreClass(matchup, self.pro_schedule, matchup_total, self.year, scoring_id) for matchup in schedule] + schedule = data["schedule"] + box_data = [ + self.BoxScoreClass( + matchup, self.pro_schedule, matchup_total, self.year, scoring_id + ) + for matchup in schedule + ] for team in self.teams: for matchup in box_data: @@ -194,8 +277,10 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat matchup.away_team = team return box_data - def player_info(self, name: str = None, playerId: Union[int, list] = None, include_news = False) -> Union[Player, List[Player]]: - ''' Returns Player class if name found ''' + def player_info( + self, name: str = None, playerId: Union[int, list] = None, include_news=False + ) -> Union[Player, List[Player]]: + """Returns Player class if name found""" if name: playerId = self.player_map.get(name) @@ -211,7 +296,20 @@ def player_info(self, name: str = None, playerId: Union[int, list] = None, inclu for id in playerId: news[id] = self.espn_request.get_player_news(id) - if len(data['players']) == 1: - return Player(data['players'][0], self.year, self.pro_schedule, news=news.get(playerId[0], []) if include_news else None) - if len(data['players']) > 1: - return [Player(player, self.year, self.pro_schedule, news=news.get(player['id'], []) if include_news else None) for player in data['players']] \ No newline at end of file + if len(data["players"]) == 1: + return Player( + data["players"][0], + self.year, + self.pro_schedule, + news=news.get(playerId[0], []) if include_news else None, + ) + if len(data["players"]) > 1: + return [ + Player( + player, + self.year, + self.pro_schedule, + news=news.get(player["id"], []) if include_news else None, + ) + for player in data["players"] + ] diff --git a/espn_api/basketball/matchup.py b/espn_api/basketball/matchup.py index 346c60482..25063b694 100644 --- a/espn_api/basketball/matchup.py +++ b/espn_api/basketball/matchup.py @@ -1,40 +1,59 @@ from .constant import STATS_MAP + class Matchup(object): - '''Creates Matchup instance''' + """Creates Matchup instance""" + def __init__(self, data): - self.winner = data['winner'] - (self.home_team, self.home_final_score, self.home_team_cats, - self.home_team_live_score) = self._fetch_matchup_info(data, 'home') - (self.away_team, self.away_final_score, self.away_team_cats, - self.away_team_live_score) = self._fetch_matchup_info(data, 'away') + self.winner = data["winner"] + ( + self.home_team, + self.home_final_score, + self.home_team_cats, + self.home_team_live_score, + ) = self._fetch_matchup_info(data, "home") + ( + self.away_team, + self.away_final_score, + self.away_team_cats, + self.away_team_live_score, + ) = self._fetch_matchup_info(data, "away") def __repr__(self): # TODO: use final score when that's available? # writing this too early to see if data['home']['totalPoints'] is final score # it might also be used for points leagues instead of category leagues if not self.away_team_live_score: - return f'Matchup({self.home_team}, {self.away_team})' + return f"Matchup({self.home_team}, {self.away_team})" else: - return f'Matchup({self.home_team} {round(self.home_team_live_score, 1)} - {round(self.away_team_live_score, 1)} {self.away_team})' + return f"Matchup({self.home_team} {round(self.home_team_live_score, 1)} - {round(self.away_team_live_score, 1)} {self.away_team})" def _fetch_matchup_info(self, data, team): - '''Fetch info for matchup''' + """Fetch info for matchup""" if team not in data: return (0, 0, None, None) - team_id = data[team]['teamId'] - final_score = data[team]['totalPoints'] + team_id = data[team]["teamId"] + final_score = data[team]["totalPoints"] team_cats = None team_live_score = None # if stats are available - if 'cumulativeScore' in data[team].keys() and data[team]['cumulativeScore']['scoreByStat']: - - team_live_score = (data[team]['cumulativeScore']['wins'] + - data[team]['cumulativeScore']['ties']/2) - - team_cats = { STATS_MAP.get(i, i): {'score': data[team]['cumulativeScore']['scoreByStat'][i]['score'], - 'result': data[team]['cumulativeScore']['scoreByStat'][i]['result']} for i in data[team]['cumulativeScore']['scoreByStat'].keys()} + if ( + "cumulativeScore" in data[team].keys() + and data[team]["cumulativeScore"]["scoreByStat"] + ): + + team_live_score = ( + data[team]["cumulativeScore"]["wins"] + + data[team]["cumulativeScore"]["ties"] / 2 + ) + + team_cats = { + STATS_MAP.get(i, i): { + "score": data[team]["cumulativeScore"]["scoreByStat"][i]["score"], + "result": data[team]["cumulativeScore"]["scoreByStat"][i]["result"], + } + for i in data[team]["cumulativeScore"]["scoreByStat"].keys() + } return (team_id, final_score, team_cats, team_live_score) - diff --git a/espn_api/basketball/player.py b/espn_api/basketball/player.py index d1a702d6f..3d3a89ecc 100644 --- a/espn_api/basketball/player.py +++ b/espn_api/basketball/player.py @@ -3,32 +3,45 @@ from datetime import datetime from functools import cached_property + class Player(object): - '''Player are part of team''' - def __init__(self, data, year, pro_team_schedule = None, news = None): - self.name = json_parsing(data, 'fullName') - self.playerId = json_parsing(data, 'id') + """Player are part of team""" + + def __init__(self, data, year, pro_team_schedule=None, news=None): + self.name = json_parsing(data, "fullName") + self.playerId = json_parsing(data, "id") self.year = year - self.position = POSITION_MAP[json_parsing(data, 'defaultPositionId') - 1] - self.lineupSlot = POSITION_MAP.get(data.get('lineupSlotId'), '') - self.eligibleSlots = [POSITION_MAP[pos] for pos in json_parsing(data, 'eligibleSlots')] - self.acquisitionType = json_parsing(data, 'acquisitionType') - self.proTeam = PRO_TEAM_MAP[json_parsing(data, 'proTeamId')] - self.injuryStatus = json_parsing(data, 'injuryStatus') - self.posRank = json_parsing(data, 'positionalRanking') + self.position = POSITION_MAP[json_parsing(data, "defaultPositionId") - 1] + self.lineupSlot = POSITION_MAP.get(data.get("lineupSlotId"), "") + self.eligibleSlots = [ + POSITION_MAP[pos] for pos in json_parsing(data, "eligibleSlots") + ] + self.acquisitionType = json_parsing(data, "acquisitionType") + self.proTeam = PRO_TEAM_MAP[json_parsing(data, "proTeamId")] + self.injuryStatus = json_parsing(data, "injuryStatus") + self.posRank = json_parsing(data, "positionalRanking") self.stats = {} self.schedule = {} self.news = {} - expected_return_date = json_parsing(data, 'expectedReturnDate') - self.expected_return_date = datetime(*expected_return_date).date() if expected_return_date else None + expected_return_date = json_parsing(data, "expectedReturnDate") + self.expected_return_date = ( + datetime(*expected_return_date).date() if expected_return_date else None + ) if pro_team_schedule: - pro_team_id = json_parsing(data, 'proTeamId') + pro_team_id = json_parsing(data, "proTeamId") pro_team = pro_team_schedule.get(pro_team_id, {}) for key in pro_team: game = pro_team[key][0] - team = game['awayProTeamId'] if game['awayProTeamId'] != pro_team_id else game['homeProTeamId'] - self.schedule[key] = { 'team': PRO_TEAM_MAP[team], 'date': datetime.fromtimestamp(game['date']/1000.0) } + team = ( + game["awayProTeamId"] + if game["awayProTeamId"] != pro_team_id + else game["homeProTeamId"] + ) + self.schedule[key] = { + "team": PRO_TEAM_MAP[team], + "date": datetime.fromtimestamp(game["date"] / 1000.0), + } if news: news_feed = news.get("news", {}).get("feed", []) @@ -36,47 +49,72 @@ def __init__(self, data, year, pro_team_schedule = None, news = None): { "published": item.get("published", ""), "headline": item.get("headline", ""), - "story": item.get("story", "") + "story": item.get("story", ""), } for item in news_feed ] # add available stats - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] - self.injuryStatus = player.get('injuryStatus', self.injuryStatus) - self.injured = player.get('injured', False) + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) + self.injuryStatus = player.get("injuryStatus", self.injuryStatus) + self.injured = player.get("injured", False) - for split in player.get('stats', []): - if split['seasonId'] == year: - id = self._stat_id_pretty(split['id'], split['scoringPeriodId']) - applied_total = split.get('appliedTotal', 0) - applied_avg = round(split.get('appliedAverage', 0), 2) + for split in player.get("stats", []): + if split["seasonId"] == year: + id = self._stat_id_pretty(split["id"], split["scoringPeriodId"]) + applied_total = split.get("appliedTotal", 0) + applied_avg = round(split.get("appliedAverage", 0), 2) game = self.schedule.get(id, {}) - self.stats[id] = dict(applied_total=applied_total, applied_avg=applied_avg, team=game.get('team', None), date=game.get('date', None)) - if split.get('stats'): - if 'averageStats' in split.keys(): - self.stats[id]['avg'] = {STATS_MAP.get(i, i): split['averageStats'][i] for i in split['averageStats'].keys() if STATS_MAP.get(i) != ''} - self.stats[id]['total'] = {STATS_MAP.get(i, i): split['stats'][i] for i in split['stats'].keys() if STATS_MAP.get(i) != ''} + self.stats[id] = dict( + applied_total=applied_total, + applied_avg=applied_avg, + team=game.get("team", None), + date=game.get("date", None), + ) + if split.get("stats"): + if "averageStats" in split.keys(): + self.stats[id]["avg"] = { + STATS_MAP.get(i, i): split["averageStats"][i] + for i in split["averageStats"].keys() + if STATS_MAP.get(i) != "" + } + self.stats[id]["total"] = { + STATS_MAP.get(i, i): split["stats"][i] + for i in split["stats"].keys() + if STATS_MAP.get(i) != "" + } else: - self.stats[id]['avg'] = None - self.stats[id]['total'] = {STATS_MAP.get(i, i): split['stats'][i] for i in split['stats'].keys() if STATS_MAP.get(i) != ''} - self.total_points = self.stats.get(f'{year}_total', {}).get('applied_total', 0) - self.avg_points = self.stats.get(f'{year}_total', {}).get('applied_avg', 0) - self.projected_total_points= self.stats.get(f'{year}_projected', {}).get('applied_total', 0) - self.projected_avg_points = self.stats.get(f'{year}_projected', {}).get('applied_avg', 0) + self.stats[id]["avg"] = None + self.stats[id]["total"] = { + STATS_MAP.get(i, i): split["stats"][i] + for i in split["stats"].keys() + if STATS_MAP.get(i) != "" + } + self.total_points = self.stats.get(f"{year}_total", {}).get("applied_total", 0) + self.avg_points = self.stats.get(f"{year}_total", {}).get("applied_avg", 0) + self.projected_total_points = self.stats.get(f"{year}_projected", {}).get( + "applied_total", 0 + ) + self.projected_avg_points = self.stats.get(f"{year}_projected", {}).get( + "applied_avg", 0 + ) def __repr__(self): - return f'Player({self.name})' + return f"Player({self.name})" def _stat_id_pretty(self, id: str, scoring_period): id_type = STAT_ID_MAP.get(id[:2]) - return f'{id[2:]}_{id_type}' if id_type else str(scoring_period) + return f"{id[2:]}_{id_type}" if id_type else str(scoring_period) @cached_property def nine_cat_averages(self): return { - k: round(v, (3 if k in {'FG%', 'FT%'} else 1)) - for k, v in self.stats.get(f'{self.year}_total', {}).get("avg", {}).items() + k: round(v, (3 if k in {"FG%", "FT%"} else 1)) + for k, v in self.stats.get(f"{self.year}_total", {}).get("avg", {}).items() if k in NINE_CAT_STATS } diff --git a/espn_api/basketball/team.py b/espn_api/basketball/team.py index fe5d7d7ca..204aa9b57 100644 --- a/espn_api/basketball/team.py +++ b/espn_api/basketball/team.py @@ -4,63 +4,70 @@ from .matchup import Matchup from .constant import STATS_MAP + class Team(object): - '''Teams are part of the league''' + """Teams are part of the league""" + def __init__(self, data, roster, schedule, year, **kwargs): - self.team_id = data['id'] - self.team_abbrev = data['abbrev'] - self.team_name = data.get('name', 'Unknown') - if self.team_name == 'Unknown': - self.team_name = "%s %s" % (data.get('location', 'Unknown'), data.get('nickname', 'Unknown')) - self.division_id = data['divisionId'] - self.division_name = '' # set by caller - self.wins = data['record']['overall']['wins'] - self.losses = data['record']['overall']['losses'] - self.ties = data['record']['overall']['ties'] - self.points_for = data['record']['overall']['pointsFor'] - self.points_against = round(data['record']['overall']['pointsAgainst'], 2) - self.acquisitions = data.get('transactionCounter', {}).get('acquisitions', 0) - self.acquisition_budget_spent = data.get('transactionCounter', {}).get('acquisitionBudgetSpent', 0) - self.drops = data.get('transactionCounter', {}).get('drops', 0) - self.trades = data.get('transactionCounter', {}).get('trades', 0) - self.logo_url = '' + self.team_id = data["id"] + self.team_abbrev = data["abbrev"] + self.team_name = data.get("name", "Unknown") + if self.team_name == "Unknown": + self.team_name = "%s %s" % ( + data.get("location", "Unknown"), + data.get("nickname", "Unknown"), + ) + self.division_id = data["divisionId"] + self.division_name = "" # set by caller + self.wins = data["record"]["overall"]["wins"] + self.losses = data["record"]["overall"]["losses"] + self.ties = data["record"]["overall"]["ties"] + self.points_for = data["record"]["overall"]["pointsFor"] + self.points_against = round(data["record"]["overall"]["pointsAgainst"], 2) + self.acquisitions = data.get("transactionCounter", {}).get("acquisitions", 0) + self.acquisition_budget_spent = data.get("transactionCounter", {}).get( + "acquisitionBudgetSpent", 0 + ) + self.drops = data.get("transactionCounter", {}).get("drops", 0) + self.trades = data.get("transactionCounter", {}).get("trades", 0) + self.logo_url = "" self.stats = None - self.standing = data['playoffSeed'] - self.final_standing = data.get('rankFinal') or data.get('rankCalculatedFinal') + self.standing = data["playoffSeed"] + self.final_standing = data.get("rankFinal") or data.get("rankCalculatedFinal") self.roster: List[Player] = [] self.schedule = [] - - if 'valuesByStat' in data: - self.stats = {STATS_MAP.get(i, i): j for i, j in data['valuesByStat'].items()} - if 'logo' in data: - self.logo_url = data['logo'] - - self._fetch_roster(roster, year, kwargs.get('pro_schedule')) + + if "valuesByStat" in data: + self.stats = { + STATS_MAP.get(i, i): j for i, j in data["valuesByStat"].items() + } + if "logo" in data: + self.logo_url = data["logo"] + + self._fetch_roster(roster, year, kwargs.get("pro_schedule")) self._fetch_schedule(schedule) - self.owners = kwargs.get('owners', []) - + self.owners = kwargs.get("owners", []) + def __repr__(self): - return f'Team({self.team_name})' - + return f"Team({self.team_name})" - def _fetch_roster(self, data, year, pro_schedule = None): - '''Fetch teams roster''' + def _fetch_roster(self, data, year, pro_schedule=None): + """Fetch teams roster""" self.roster.clear() - roster = data['entries'] + roster = data["entries"] for player in roster: self.roster.append(Player(player, year, pro_schedule)) - def _fetch_schedule(self, data): - '''Fetch schedule and scores for team''' + """Fetch schedule and scores for team""" for match in data: - if 'away' in match.keys(): - if match.get('away', {}).get('teamId', -1) == self.team_id: + if "away" in match.keys(): + if match.get("away", {}).get("teamId", -1) == self.team_id: new_match = Matchup(match) - setattr(new_match, 'away_team', self) + setattr(new_match, "away_team", self) self.schedule.append(new_match) - elif match.get('home', {}).get('teamId', -1) == self.team_id: + elif match.get("home", {}).get("teamId", -1) == self.team_id: new_match = Matchup(match) - setattr(new_match, 'home_team', self) + setattr(new_match, "home_team", self) self.schedule.append(new_match) diff --git a/espn_api/basketball/transaction.py b/espn_api/basketball/transaction.py index ae0e5cf71..326cbf3f6 100644 --- a/espn_api/basketball/transaction.py +++ b/espn_api/basketball/transaction.py @@ -1,23 +1,24 @@ class Transaction(object): def __init__(self, data, player_map, get_team_data): - self.team = get_team_data(data['teamId']) - self.type = data['type'] - self.status = data['status'] - self.scoring_period = data['scoringPeriodId'] - self.date = data.get('processDate') - self.bid_amount = data.get('bidAmount') + self.team = get_team_data(data["teamId"]) + self.type = data["type"] + self.status = data["status"] + self.scoring_period = data["scoringPeriodId"] + self.date = data.get("processDate") + self.bid_amount = data.get("bidAmount") self.items = [] - for item in data['items']: + for item in data["items"]: self.items.append(TransactionItem(item, player_map)) def __repr__(self): - items = ', '.join([str(item) for item in self.items]) - return f'Transaction({self.team.team_name} {self.type} {items})' + items = ", ".join([str(item) for item in self.items]) + return f"Transaction({self.team.team_name} {self.type} {items})" + class TransactionItem(object): def __init__(self, data, player_map): - self.type = data['type'] - self.player = player_map[data['playerId']] + self.type = data["type"] + self.player = player_map[data["playerId"]] def __repr__(self): - return f'{self.type} {self.player}' + return f"{self.type} {self.player}" diff --git a/espn_api/football/__init__.py b/espn_api/football/__init__.py index dfb16ff3a..7a6aa75da 100644 --- a/espn_api/football/__init__.py +++ b/espn_api/football/__init__.py @@ -1,12 +1,7 @@ -__all__ = ['League', - 'Team', - 'Matchup', - 'Player', - 'BoxPlayer' - ] +__all__ = ["League", "Team", "Matchup", "Player", "BoxPlayer"] from .league import League from .team import Team from .matchup import Matchup from .player import Player -from .box_player import BoxPlayer \ No newline at end of file +from .box_player import BoxPlayer diff --git a/espn_api/football/activity.py b/espn_api/football/activity.py index 22a0b9379..752f7f98c 100644 --- a/espn_api/football/activity.py +++ b/espn_api/football/activity.py @@ -1,66 +1,66 @@ from .constant import ACTIVITY_MAP + class Activity(object): def __init__(self, data, player_map, get_team_data, player_info): - self.actions = [] # List of tuples (Team, action, Player) - self.date = data['date'] - for msg in data['messages']: - msg_id = msg['messageTypeId'] + self.actions = [] # List of tuples (Team, action, Player) + self.date = data["date"] + for msg in data["messages"]: + msg_id = msg["messageTypeId"] # Trades: emit two rows — TRADE_SENT (from) + TRADE_RECEIVED (to) if msg_id == 244: - from_team = get_team_data(msg['from']) - to_team = get_team_data(msg.get('to')) + from_team = get_team_data(msg["from"]) + to_team = get_team_data(msg.get("to")) player = None if from_team: for team_player in from_team.roster: - if team_player.playerId == msg['targetId']: + if team_player.playerId == msg["targetId"]: player = team_player break if not player and to_team: for team_player in to_team.roster: - if team_player.playerId == msg['targetId']: + if team_player.playerId == msg["targetId"]: player = team_player break if not player: - player = player_info(playerId=msg['targetId']) + player = player_info(playerId=msg["targetId"]) if not player: - player = msg.get('targetId', 'Unknown') + player = msg.get("targetId", "Unknown") - self.actions.append((from_team, 'TRADE_SENT', player, 0)) + self.actions.append((from_team, "TRADE_SENT", player, 0)) if to_team: - self.actions.append((to_team, 'TRADE_RECEIVED', player, 0)) + self.actions.append((to_team, "TRADE_RECEIVED", player, 0)) continue # Non-trade messages - team = '' - action = 'UNKNOWN' + team = "" + action = "UNKNOWN" player = None bid_amount = 0 if msg_id == 239: - team = get_team_data(msg['for']) + team = get_team_data(msg["for"]) else: - team = get_team_data(msg['to']) + team = get_team_data(msg["to"]) if msg_id in ACTIVITY_MAP: action = ACTIVITY_MAP[msg_id] - if action == 'WAIVER ADDED': - bid_amount = msg.get('from', 0) + if action == "WAIVER ADDED": + bid_amount = msg.get("from", 0) if team: for team_player in team.roster: - if team_player.playerId == msg['targetId']: + if team_player.playerId == msg["targetId"]: player = team_player break if not player: - player = player_info(playerId=msg['targetId']) + player = player_info(playerId=msg["targetId"]) if not player: - player = msg.get('targetId', 'Unknown') + player = msg.get("targetId", "Unknown") self.actions.append((team, action, player, bid_amount)) def __repr__(self): - return 'Activity(' + ' '.join("(%s,%s,%s)" % tup[0:3] for tup in self.actions) + ')' - - - - - + return ( + "Activity(" + + " ".join("(%s,%s,%s)" % tup[0:3] for tup in self.actions) + + ")" + ) diff --git a/espn_api/football/box_player.py b/espn_api/football/box_player.py index 6be7c738f..4598ea3fc 100644 --- a/espn_api/football/box_player.py +++ b/espn_api/football/box_player.py @@ -4,33 +4,48 @@ class BoxPlayer(Player): - '''player with extra data from a matchup''' - def __init__(self, data, pro_schedule, positional_rankings, week, year, player_team_cache=None): + """player with extra data from a matchup""" + + def __init__( + self, + data, + pro_schedule, + positional_rankings, + week, + year, + player_team_cache=None, + ): super(BoxPlayer, self).__init__(data, year) - self.slot_position = 'FA' - self.pro_opponent = "None" # professional team playing against - self.pro_pos_rank = 0 # rank of professional team against player position - self.game_played = 100 # 0-100 for percent of game played + self.slot_position = "FA" + self.pro_opponent = "None" # professional team playing against + self.pro_pos_rank = 0 # rank of professional team against player position + self.game_played = 100 # 0-100 for percent of game played self.on_bye_week = False - if 'lineupSlotId' in data: - self.slot_position = POSITION_MAP[data['lineupSlotId']] + if "lineupSlotId" in data: + self.slot_position = POSITION_MAP[data["lineupSlotId"]] - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) # ESPN's top-level proTeamId is the player's CURRENT team, not their # team at time of the game. Always prefer the per-week proTeamId from # the actual stats entry, which has the correct team per scoring period. - pro_team_id = player['proTeamId'] - player_stats = player.get('stats', []) + pro_team_id = player["proTeamId"] + player_stats = player.get("stats", []) # Check for an actual (statSourceId=0) entry for this week found_actual = False for stat in player_stats: - if (stat.get('scoringPeriodId') == week - and stat.get('statSourceId') == 0 - and stat.get('proTeamId', 0) != 0): - pro_team_id = stat['proTeamId'] + if ( + stat.get("scoringPeriodId") == week + and stat.get("statSourceId") == 0 + and stat.get("proTeamId", 0) != 0 + ): + pro_team_id = stat["proTeamId"] self.proTeam = PRO_TEAM_MAP.get(pro_team_id, self.proTeam) found_actual = True break @@ -47,23 +62,29 @@ def __init__(self, data, pro_schedule, positional_rankings, week, year, player_t player_team_cache[self.playerId] = pro_team_id if pro_team_id in pro_schedule: - (opp_id, date) = pro_schedule[pro_team_id] - self.game_date = datetime.fromtimestamp(date/1000.0) - self.game_played = 100 if datetime.now() > self.game_date + timedelta(hours=3) else 0 - posId = str(player['defaultPositionId']) + opp_id, date = pro_schedule[pro_team_id] + self.game_date = datetime.fromtimestamp(date / 1000.0) + self.game_played = ( + 100 if datetime.now() > self.game_date + timedelta(hours=3) else 0 + ) + posId = str(player["defaultPositionId"]) if posId in positional_rankings: self.pro_opponent = PRO_TEAM_MAP[opp_id] - self.pro_pos_rank = positional_rankings[posId][str(opp_id)] if str(opp_id) in positional_rankings[posId] else 0 - else: # bye week + self.pro_pos_rank = ( + positional_rankings[posId][str(opp_id)] + if str(opp_id) in positional_rankings[posId] + else 0 + ) + else: # bye week self.on_bye_week = True stats = self.stats.get(week, {}) - self.points = stats.get('points', 0) - self.breakdown = stats.get('breakdown', {}) - self.points_breakdown = stats.get('points_breakdown', {}) - self.projected_points = stats.get('projected_points', 0) - self.projected_breakdown = stats.get('projected_breakdown', {}) - self.projected_points_breakdown = stats.get('projected_points_breakdown', {}) + self.points = stats.get("points", 0) + self.breakdown = stats.get("breakdown", {}) + self.points_breakdown = stats.get("points_breakdown", {}) + self.projected_points = stats.get("projected_points", 0) + self.projected_breakdown = stats.get("projected_breakdown", {}) + self.projected_points_breakdown = stats.get("projected_points_breakdown", {}) def __repr__(self): - return f'Player({self.name}, points:{self.points}, projected:{self.projected_points})' + return f"Player({self.name}, points:{self.points}, projected:{self.projected_points})" diff --git a/espn_api/football/box_score.py b/espn_api/football/box_score.py index 20b048487..f91f8b834 100644 --- a/espn_api/football/box_score.py +++ b/espn_api/football/box_score.py @@ -1,43 +1,91 @@ from .box_player import BoxPlayer + class BoxScore(object): - ''' ''' - def __init__(self, data, pro_schedule, positional_rankings, week, year, player_team_cache=None): - self.matchup_type = data.get('playoffTierType', 'NONE') - self.is_playoff = self.matchup_type != 'NONE' + """ """ + + def __init__( + self, + data, + pro_schedule, + positional_rankings, + week, + year, + player_team_cache=None, + ): + self.matchup_type = data.get("playoffTierType", "NONE") + self.is_playoff = self.matchup_type != "NONE" - (self.home_team, self.home_score, self.home_projected, self.home_lineup) = self._get_team_data('home', data, pro_schedule, positional_rankings, week, year, player_team_cache) - self.home_projected = self._get_projected_score(self.home_projected, self.home_lineup) + self.home_team, self.home_score, self.home_projected, self.home_lineup = ( + self._get_team_data( + "home", + data, + pro_schedule, + positional_rankings, + week, + year, + player_team_cache, + ) + ) + self.home_projected = self._get_projected_score( + self.home_projected, self.home_lineup + ) - (self.away_team, self.away_score, self.away_projected, self.away_lineup) = self._get_team_data('away', data, pro_schedule, positional_rankings, week, year, player_team_cache) - self.away_projected = self._get_projected_score(self.away_projected, self.away_lineup) + self.away_team, self.away_score, self.away_projected, self.away_lineup = ( + self._get_team_data( + "away", + data, + pro_schedule, + positional_rankings, + week, + year, + player_team_cache, + ) + ) + self.away_projected = self._get_projected_score( + self.away_projected, self.away_lineup + ) def __repr__(self): away_team = self.away_team or "BYE" home_team = self.home_team or "BYE" - return f'Box Score({away_team} at {home_team})' - + return f"Box Score({away_team} at {home_team})" + def _get_projected_score(self, projected_score, lineup): - if projected_score != -1: + if projected_score != -1: + return projected_score + projected_score = 0 + for player in lineup: + if player.slot_position != "BE" and player.slot_position != "IR": + projected_score += player.projected_points return projected_score - projected_score = 0 - for player in lineup: - if player.slot_position != 'BE' and player.slot_position != 'IR': - projected_score += player.projected_points - return projected_score - - def _get_team_data(self, team, data, pro_schedule, positional_rankings, week, year, player_team_cache=None): - if team not in data: - return (None, 0, -1, []) - - team_id = data[team]['teamId'] - team_projected = -1 - if 'totalPointsLive' in data[team]: - team_score = round(data[team]['totalPointsLive'], 2) - team_projected = round(data[team].get('totalProjectedPointsLive', -1), 2) - else: - team_score = round(data[team]['totalPoints'], 2) - team_roster = data[team]['rosterForCurrentScoringPeriod']['entries'] - team_lineup = [BoxPlayer(player, pro_schedule, positional_rankings, week, year, player_team_cache) for player in team_roster] - - return (team_id, team_score, team_projected, team_lineup) \ No newline at end of file + + def _get_team_data( + self, + team, + data, + pro_schedule, + positional_rankings, + week, + year, + player_team_cache=None, + ): + if team not in data: + return (None, 0, -1, []) + + team_id = data[team]["teamId"] + team_projected = -1 + if "totalPointsLive" in data[team]: + team_score = round(data[team]["totalPointsLive"], 2) + team_projected = round(data[team].get("totalProjectedPointsLive", -1), 2) + else: + team_score = round(data[team]["totalPoints"], 2) + team_roster = data[team]["rosterForCurrentScoringPeriod"]["entries"] + team_lineup = [ + BoxPlayer( + player, pro_schedule, positional_rankings, week, year, player_team_cache + ) + for player in team_roster + ] + + return (team_id, team_score, team_projected, team_lineup) diff --git a/espn_api/football/constant.py b/espn_api/football/constant.py index bad339a84..d1824b247 100644 --- a/espn_api/football/constant.py +++ b/espn_api/football/constant.py @@ -1,523 +1,507 @@ POSITION_MAP = { - 0: 'QB', - 1: 'TQB', - 2: 'RB', - 3: 'RB/WR', - 4: 'WR', - 5: 'WR/TE', - 6: 'TE', - 7: 'OP', - 8: 'DT', - 9: 'DE', - 10: 'LB', - 11: 'DL', - 12: 'CB', - 13: 'S', - 14: 'DB', - 15: 'DP', - 16: 'D/ST', - 17: 'K', - 18: 'P', - 19: 'HC', - 20: 'BE', - 21: 'IR', - 22: '', - 23: 'RB/WR/TE', - 24: 'ER', - 25: 'Rookie', - 'QB': 0, - 'RB': 2, - 'WR': 4, - 'TE': 6, - 'D/ST': 16, - 'K': 17, - 'FLEX': 23, - 'DT': 8, - 'DE': 9, - 'LB': 10, - 'DL': 11, - 'CB': 12, - 'S': 13, - 'DB': 14, - 'DP': 15, - 'HC': 19 + 0: "QB", + 1: "TQB", + 2: "RB", + 3: "RB/WR", + 4: "WR", + 5: "WR/TE", + 6: "TE", + 7: "OP", + 8: "DT", + 9: "DE", + 10: "LB", + 11: "DL", + 12: "CB", + 13: "S", + 14: "DB", + 15: "DP", + 16: "D/ST", + 17: "K", + 18: "P", + 19: "HC", + 20: "BE", + 21: "IR", + 22: "", + 23: "RB/WR/TE", + 24: "ER", + 25: "Rookie", + "QB": 0, + "RB": 2, + "WR": 4, + "TE": 6, + "D/ST": 16, + "K": 17, + "FLEX": 23, + "DT": 8, + "DE": 9, + "LB": 10, + "DL": 11, + "CB": 12, + "S": 13, + "DB": 14, + "DP": 15, + "HC": 19, } PRO_TEAM_MAP = { - 0 : 'None', - 1 : 'ATL', - 2 : 'BUF', - 3 : 'CHI', - 4 : 'CIN', - 5 : 'CLE', - 6 : 'DAL', - 7 : 'DEN', - 8 : 'DET', - 9 : 'GB', - 10: 'TEN', - 11: 'IND', - 12: 'KC', - 13: 'LV', - 14: 'LAR', - 15: 'MIA', - 16: 'MIN', - 17: 'NE', - 18: 'NO', - 19: 'NYG', - 20: 'NYJ', - 21: 'PHI', - 22: 'ARI', - 23: 'PIT', - 24: 'LAC', - 25: 'SF', - 26: 'SEA', - 27: 'TB', - 28: 'WSH', - 29: 'CAR', - 30: 'JAX', - 33: 'BAL', - 34: 'HOU' + 0: "None", + 1: "ATL", + 2: "BUF", + 3: "CHI", + 4: "CIN", + 5: "CLE", + 6: "DAL", + 7: "DEN", + 8: "DET", + 9: "GB", + 10: "TEN", + 11: "IND", + 12: "KC", + 13: "LV", + 14: "LAR", + 15: "MIA", + 16: "MIN", + 17: "NE", + 18: "NO", + 19: "NYG", + 20: "NYJ", + 21: "PHI", + 22: "ARI", + 23: "PIT", + 24: "LAC", + 25: "SF", + 26: "SEA", + 27: "TB", + 28: "WSH", + 29: "CAR", + 30: "JAX", + 33: "BAL", + 34: "HOU", } ACTIVITY_MAP = { - 178: 'FA ADDED', - 180: 'WAIVER ADDED', - 179: 'DROPPED', - 181: 'DROPPED', - 239: 'DROPPED', - 244: 'TRADED', - 'FA': 178, - 'WAIVER': 180, - 'TRADED': 244 + 178: "FA ADDED", + 180: "WAIVER ADDED", + 179: "DROPPED", + 181: "DROPPED", + 239: "DROPPED", + 244: "TRADED", + "FA": 178, + "WAIVER": 180, + "TRADED": 244, } PLAYER_STATS_MAP = { # Passing Stats - 0: 'passingAttempts', # PA - 1: 'passingCompletions', # PC - 2: 'passingIncompletions', # INC - 3: 'passingYards', # PY - 4: 'passingTouchdowns', # PTD + 0: "passingAttempts", # PA + 1: "passingCompletions", # PC + 2: "passingIncompletions", # INC + 3: "passingYards", # PY + 4: "passingTouchdowns", # PTD # 5-14 appear for passing players # 5-7: 6 is half of 5 (integer divide by 2), 7 is half of 6 (integer divide by 2) # 8-10: 9 is half of 8 (integer divide by 2), 10 is half of 9 (integer divide by 2) # 11-12: 12 is half of 11 (integer divide by 2) # 13-14: 14 is half of 13 (integer divide by 2) - 15: 'passing40PlusYardTD', # PTD40 - 16: 'passing50PlusYardTD', # PTD50 - 17: 'passing300To399YardGame', # P300 - 18: 'passing400PlusYardGame', # P400 - 19: 'passing2PtConversions', # 2PC - 20: 'passingInterceptions', # INT - 21: 'passingCompletionPercentage', - 22: 'passingYards', # PY - TODO: figure out what the difference is between 22 and 3 - + 15: "passing40PlusYardTD", # PTD40 + 16: "passing50PlusYardTD", # PTD50 + 17: "passing300To399YardGame", # P300 + 18: "passing400PlusYardGame", # P400 + 19: "passing2PtConversions", # 2PC + 20: "passingInterceptions", # INT + 21: "passingCompletionPercentage", + 22: "passingYards", # PY - TODO: figure out what the difference is between 22 and 3 # Rushing Stats - 23: 'rushingAttempts', # RA - 24: 'rushingYards', # RY - 25: 'rushingTouchdowns', # RTD - 26: 'rushing2PtConversions', # 2PR + 23: "rushingAttempts", # RA + 24: "rushingYards", # RY + 25: "rushingTouchdowns", # RTD + 26: "rushing2PtConversions", # 2PR # 27-34 appear for rushing players # 27-29: 28 is half of 27 (integer divide by 2), 29 is half of 28 (integer divide by 2) # 30-32: 31 is half of 30 (integer divide by 2), 32 is half of 31 (integer divide by 2) # 33-34: 34 is half of 33 (integer divide by 2) - 35: 'rushing40PlusYardTD', # RTD40 - 36: 'rushing50PlusYardTD', # RTD50 - 37: 'rushing100To199YardGame', # RY100 - 38: 'rushing200PlusYardGame', # RY200 - 39: 'rushingYardsPerAttempt', - 40: 'rushingYards', # RY - TODO: figure out what the difference is between 40 and 24 - + 35: "rushing40PlusYardTD", # RTD40 + 36: "rushing50PlusYardTD", # RTD50 + 37: "rushing100To199YardGame", # RY100 + 38: "rushing200PlusYardGame", # RY200 + 39: "rushingYardsPerAttempt", + 40: "rushingYards", # RY - TODO: figure out what the difference is between 40 and 24 # Receiving Stats - 41: 'receivingReceptions', # REC - 42: 'receivingYards', # REY - 43: 'receivingTouchdowns', # RETD - 44: 'receiving2PtConversions', # 2PRE - 45: 'receiving40PlusYardTD', # RETD40 - 46: 'receiving50PlusYardTD', # RETD50 + 41: "receivingReceptions", # REC + 42: "receivingYards", # REY + 43: "receivingTouchdowns", # RETD + 44: "receiving2PtConversions", # 2PRE + 45: "receiving40PlusYardTD", # RETD40 + 46: "receiving50PlusYardTD", # RETD50 # 47-52 appear for receiving players # 47-49: 48 is half of 47 (integer divide by 2), 49 is half of 48 (integer divide by 2) # 50-52: 51 is half of 50 (integer divide by 2), 52 is half of 51 (integer divide by 2) - 53: 'receivingReceptions', # REC - TODO: figure out what the difference is between 53 and 41 + 53: "receivingReceptions", # REC - TODO: figure out what the difference is between 53 and 41 # 54-55 appear for receiving players # 54-55: 55 is half of 54 (integer divide by 2) - 56: 'receiving100To199YardGame', # REY100 - 57: 'receiving200PlusYardGame', # REY200 - 58: 'receivingTargets', # RET - 59: 'receivingYardsAfterCatch', - 60: 'receivingYardsPerReception', - 61: 'receivingYards', # REY - TODO: figure out what the difference is between 61 and 42 - 62: '2PtConversions', - 63: 'fumbleRecoveredForTD', # FTD - 64: 'passingTimesSacked', # SK - - 68: 'fumbles', # FUM - - 72: 'lostFumbles', # FUML - 73: 'turnovers', - + 56: "receiving100To199YardGame", # REY100 + 57: "receiving200PlusYardGame", # REY200 + 58: "receivingTargets", # RET + 59: "receivingYardsAfterCatch", + 60: "receivingYardsPerReception", + 61: "receivingYards", # REY - TODO: figure out what the difference is between 61 and 42 + 62: "2PtConversions", + 63: "fumbleRecoveredForTD", # FTD + 64: "passingTimesSacked", # SK + 68: "fumbles", # FUM + 72: "lostFumbles", # FUML + 73: "turnovers", # Kicking Stats - 74: 'madeFieldGoalsFrom50Plus', # FG50 (does not map directly to FG50 as FG50 does not include 60+) - 75: 'attemptedFieldGoalsFrom50Plus', # FGA50 (does not map directly to FGA50 as FG50 does not include 60+) - 76: 'missedFieldGoalsFrom50Plus', # FGM50 (does not map directly to FGM50 as FG50 does not include 60+) - 77: 'madeFieldGoalsFrom40To49', # FG40 - 78: 'attemptedFieldGoalsFrom40To49', # FGA40 - 79: 'missedFieldGoalsFrom40To49', # FGM40 - 80: 'madeFieldGoalsFromUnder40', # FG0 - 81: 'attemptedFieldGoalsFromUnder40', # FGA0 - 82: 'missedFieldGoalsFromUnder40', # FGM0 - 83: 'madeFieldGoals', # FG - 84: 'attemptedFieldGoals', # FGA - 85: 'missedFieldGoals', # FGM - 86: 'madeExtraPoints', # PAT - 87: 'attemptedExtraPoints', # PATA - 88: 'missedExtraPoints', # PATM - + 74: "madeFieldGoalsFrom50Plus", # FG50 (does not map directly to FG50 as FG50 does not include 60+) + 75: "attemptedFieldGoalsFrom50Plus", # FGA50 (does not map directly to FGA50 as FG50 does not include 60+) + 76: "missedFieldGoalsFrom50Plus", # FGM50 (does not map directly to FGM50 as FG50 does not include 60+) + 77: "madeFieldGoalsFrom40To49", # FG40 + 78: "attemptedFieldGoalsFrom40To49", # FGA40 + 79: "missedFieldGoalsFrom40To49", # FGM40 + 80: "madeFieldGoalsFromUnder40", # FG0 + 81: "attemptedFieldGoalsFromUnder40", # FGA0 + 82: "missedFieldGoalsFromUnder40", # FGM0 + 83: "madeFieldGoals", # FG + 84: "attemptedFieldGoals", # FGA + 85: "missedFieldGoals", # FGM + 86: "madeExtraPoints", # PAT + 87: "attemptedExtraPoints", # PATA + 88: "missedExtraPoints", # PATM # Defensive Stats - 89: 'defensive0PointsAllowed', # PA0 - 90: 'defensive1To6PointsAllowed', # PA1 - 91: 'defensive7To13PointsAllowed', # PA7 - 92: 'defensive14To17PointsAllowed', # PA14 - 93: 'defensiveBlockedKickForTouchdowns', # BLKKRTD - 94: 'defensiveTouchdowns', # Does not include defensive blocked kick for touchdowns (BLKKRTD) - 95: 'defensiveInterceptions', # INT - 96: 'defensiveFumbles', # FR - 97: 'defensiveBlockedKicks', # BLKK - 98: 'defensiveSafeties', # SF - 99: 'defensiveSacks', # SK + 89: "defensive0PointsAllowed", # PA0 + 90: "defensive1To6PointsAllowed", # PA1 + 91: "defensive7To13PointsAllowed", # PA7 + 92: "defensive14To17PointsAllowed", # PA14 + 93: "defensiveBlockedKickForTouchdowns", # BLKKRTD + 94: "defensiveTouchdowns", # Does not include defensive blocked kick for touchdowns (BLKKRTD) + 95: "defensiveInterceptions", # INT + 96: "defensiveFumbles", # FR + 97: "defensiveBlockedKicks", # BLKK + 98: "defensiveSafeties", # SF + 99: "defensiveSacks", # SK # 100: This appears to be defensiveSacks * 2 - 101: 'kickoffReturnTouchdowns', # KRTD - 102: 'puntReturnTouchdowns', # PRTD - 103: 'interceptionReturnTouchdowns', # INTTD - 104: 'fumbleReturnTouchdowns', # FRTD - 105: 'defensivePlusSpecialTeamsTouchdowns', # Includes defensive blocked kick for touchdowns (BLKKRTD) and kickoff/punt return touchdowns - 106: 'defensiveForcedFumbles', # FF - 107: 'defensiveAssistedTackles', # TKA - 108: 'defensiveSoloTackles', # TKS - 109: 'defensiveTotalTackles', # TK - - 113: 'defensivePassesDefensed', # PD - 114: 'kickoffReturnYards', # KR - 115: 'puntReturnYards', # PR - - 118: 'puntsReturned', # PTR - - 120: 'defensivePointsAllowed', # PA - 121: 'defensive18To21PointsAllowed', # PA18 - 122: 'defensive22To27PointsAllowed', # PA22 - 123: 'defensive28To34PointsAllowed', # PA28 - 124: 'defensive35To45PointsAllowed', # PA35 - 125: 'defensive45PlusPointsAllowed', # PA46 - - 127: 'defensiveYardsAllowed', # YA - 128: 'defensiveLessThan100YardsAllowed', #YA100 - 129: 'defensive100To199YardsAllowed', # YA199 - 130: 'defensive200To299YardsAllowed', # YA299 - 131: 'defensive300To349YardsAllowed', # YA349 - 132: 'defensive350To399YardsAllowed', # YA399 - 133: 'defensive400To449YardsAllowed', # YA449 - 134: 'defensive450To499YardsAllowed', # YA499 - 135: 'defensive500To549YardsAllowed', # YA549 - 136: 'defensive550PlusYardsAllowed', # YA550 - + 101: "kickoffReturnTouchdowns", # KRTD + 102: "puntReturnTouchdowns", # PRTD + 103: "interceptionReturnTouchdowns", # INTTD + 104: "fumbleReturnTouchdowns", # FRTD + 105: "defensivePlusSpecialTeamsTouchdowns", # Includes defensive blocked kick for touchdowns (BLKKRTD) and kickoff/punt return touchdowns + 106: "defensiveForcedFumbles", # FF + 107: "defensiveAssistedTackles", # TKA + 108: "defensiveSoloTackles", # TKS + 109: "defensiveTotalTackles", # TK + 113: "defensivePassesDefensed", # PD + 114: "kickoffReturnYards", # KR + 115: "puntReturnYards", # PR + 118: "puntsReturned", # PTR + 120: "defensivePointsAllowed", # PA + 121: "defensive18To21PointsAllowed", # PA18 + 122: "defensive22To27PointsAllowed", # PA22 + 123: "defensive28To34PointsAllowed", # PA28 + 124: "defensive35To45PointsAllowed", # PA35 + 125: "defensive45PlusPointsAllowed", # PA46 + 127: "defensiveYardsAllowed", # YA + 128: "defensiveLessThan100YardsAllowed", # YA100 + 129: "defensive100To199YardsAllowed", # YA199 + 130: "defensive200To299YardsAllowed", # YA299 + 131: "defensive300To349YardsAllowed", # YA349 + 132: "defensive350To399YardsAllowed", # YA399 + 133: "defensive400To449YardsAllowed", # YA449 + 134: "defensive450To499YardsAllowed", # YA499 + 135: "defensive500To549YardsAllowed", # YA549 + 136: "defensive550PlusYardsAllowed", # YA550 # Punter Stats - 138: 'netPunts', # PT - 139: 'puntYards', # PTY - 140: 'puntsInsideThe10', # PT10 - 141: 'puntsInsideThe20', # PT20 - 142: 'blockedPunts', # PTB - 145: 'puntTouchbacks', # PTTB - 146: 'puntFairCatches', #PTFC - 147: 'puntAverage', - 148: 'puntAverage44.0+', # PTA44 - 149: 'puntAverage42.0-43.9', #PTA42 - 150: 'puntAverage40.0-41.9', #PTA40 - 151: 'puntAverage38.0-39.9', #PTA38 - 152: 'puntAverage36.0-37.9', #PTA36 - 153: 'puntAverage34.0-35.9', #PTA34 - 154: 'puntAverage33.9AndUnder', #PTA33 - + 138: "netPunts", # PT + 139: "puntYards", # PTY + 140: "puntsInsideThe10", # PT10 + 141: "puntsInsideThe20", # PT20 + 142: "blockedPunts", # PTB + 145: "puntTouchbacks", # PTTB + 146: "puntFairCatches", # PTFC + 147: "puntAverage", + 148: "puntAverage44.0+", # PTA44 + 149: "puntAverage42.0-43.9", # PTA42 + 150: "puntAverage40.0-41.9", # PTA40 + 151: "puntAverage38.0-39.9", # PTA38 + 152: "puntAverage36.0-37.9", # PTA36 + 153: "puntAverage34.0-35.9", # PTA34 + 154: "puntAverage33.9AndUnder", # PTA33 # Head Coach Stats - 155: 'teamWin', # TW - 156: 'teamLoss', # TL - 157: 'teamTie', # TIE - 158: 'pointsScored', # PTS - - 160: 'pointsMargin', - 161: '25+pointWinMargin', # WM25 - 162: '20-24pointWinMargin', # WM20 - 163: '15-19pointWinMargin', # WM15 - 164: '10-14pointWinMargin', # WM10 - 165: '5-9pointWinMargin', # WM5 - 166: '1-4pointWinMargin', # WM1 - 167: '1-4pointLossMargin', # LM1 - 168: '5-9pointLossMargin', # LM5 - 169: '10-14pointLossMargin', # LM10 - 170: '15-19pointLossMargin', # LM15 - 171: '20-24pointLossMargin', # LM20 - 172: '25+pointLossMargin', # LM25 - 174: 'winPercentage', # Value goes from 0-1 - - 187: 'defensivePointsAllowed', # TODO: figure out what the difference is between 187 and 120 - - 201: 'madeFieldGoalsFrom60Plus', # FG60 - 202: 'attemptedFieldGoalsFrom60Plus', # FGA60 - 203: 'missedFieldGoalsFrom60Plus', # FGM60 - - 205: 'defensive2PtReturns', # 2PTRET - 206: 'defensive2PtReturns', # 2PTRET - TODO: figure out what the difference is between 206 and 205 + 155: "teamWin", # TW + 156: "teamLoss", # TL + 157: "teamTie", # TIE + 158: "pointsScored", # PTS + 160: "pointsMargin", + 161: "25+pointWinMargin", # WM25 + 162: "20-24pointWinMargin", # WM20 + 163: "15-19pointWinMargin", # WM15 + 164: "10-14pointWinMargin", # WM10 + 165: "5-9pointWinMargin", # WM5 + 166: "1-4pointWinMargin", # WM1 + 167: "1-4pointLossMargin", # LM1 + 168: "5-9pointLossMargin", # LM5 + 169: "10-14pointLossMargin", # LM10 + 170: "15-19pointLossMargin", # LM15 + 171: "20-24pointLossMargin", # LM20 + 172: "25+pointLossMargin", # LM25 + 174: "winPercentage", # Value goes from 0-1 + 187: "defensivePointsAllowed", # TODO: figure out what the difference is between 187 and 120 + 201: "madeFieldGoalsFrom60Plus", # FG60 + 202: "attemptedFieldGoalsFrom60Plus", # FGA60 + 203: "missedFieldGoalsFrom60Plus", # FGM60 + 205: "defensive2PtReturns", # 2PTRET + 206: "defensive2PtReturns", # 2PTRET - TODO: figure out what the difference is between 206 and 205 } SETTINGS_SCORING_FORMAT_MAP = { - 0: { 'abbr': 'PA', 'label': 'Each Pass Attempted' }, - 1: { 'abbr': 'PC', 'label': 'Each Pass Completed' }, - 2: { 'abbr': 'INC', 'label': 'Each Incomplete Pass' }, - 3: { 'abbr': 'PY', 'label': 'Passing Yards' }, - 4: { 'abbr': 'PTD', 'label': 'TD Pass' }, - 5: { 'abbr': 'PY5', 'label': 'Every 5 passing yards' }, - 6: { 'abbr': 'PY10', 'label': 'Every 10 passing yards' }, - 7: { 'abbr': 'PY20', 'label': 'Every 20 passing yards' }, - 8: { 'abbr': 'PY25', 'label': 'Every 25 passing yards' }, - 9: { 'abbr': 'PY50', 'label': 'Every 50 passing yards' }, - 10: { 'abbr': 'PY100', 'label': 'Every 100 passing yards' }, - 11: { 'abbr': 'PC5', 'label': 'Every 5 pass completions' }, - 12: { 'abbr': 'PC10', 'label': 'Every 10 pass completions' }, - 13: { 'abbr': 'IP5', 'label': 'Every 5 pass incompletions' }, - 14: { 'abbr': 'IP10', 'label': 'Every 10 pass incompletions' }, - 15: { 'abbr': 'PTD40', 'label': '40+ yard TD pass bonus' }, - 16: { 'abbr': 'PTD50', 'label': '50+ yard TD pass bonus' }, - 17: { 'abbr': 'P300', 'label': '300-399 yard passing game' }, - 18: { 'abbr': 'P400', 'label': '400+ yard passing game' }, - 19: { 'abbr': '2PC', 'label': '2pt Passing Conversion' }, - 20: { 'abbr': 'INTT', 'label': 'Interceptions Thrown' }, - 21: { 'abbr': 'CPCT', 'label': 'Passing Completion Pct' }, - 22: { 'abbr': 'PYPG', 'label': 'Passing Yards Per Game' }, - 23: { 'abbr': 'RA', 'label': 'Rushing Attempts' }, - 24: { 'abbr': 'RY', 'label': 'Rushing Yards' }, - 25: { 'abbr': 'RTD', 'label': 'TD Rush' }, - 26: { 'abbr': '2PR', 'label': '2pt Rushing Conversion' }, - 27: { 'abbr': 'RY5', 'label': 'Every 5 rushing yards' }, - 28: { 'abbr': 'RY10', 'label': 'Every 10 rushing yards' }, - 29: { 'abbr': 'RY20', 'label': 'Every 20 rushing yards' }, - 30: { 'abbr': 'RY25', 'label': 'Every 25 rushing yards' }, - 31: { 'abbr': 'RY50', 'label': 'Every 50 rushing yards' }, - 32: { 'abbr': 'R100', 'label': 'Every 100 rushing yards' }, - 33: { 'abbr': 'RA5', 'label': 'Every 5 rush attempts' }, - 34: { 'abbr': 'RA10', 'label': 'Every 10 rush attempts' }, - 35: { 'abbr': 'RTD40', 'label': '40+ yard TD rush bonus' }, - 36: { 'abbr': 'RTD50', 'label': '50+ yard TD rush bonus' }, - 37: { 'abbr': 'RY100', 'label': '100-199 yard rushing game' }, - 38: { 'abbr': 'RY200', 'label': '200+ yard rushing game' }, - 39: { 'abbr': 'RYPA', 'label': 'Rushing Yards Per Attempt' }, - 40: { 'abbr': 'RYPG', 'label': 'Rushing Yards Per Game' }, - 41: { 'abbr': 'RECS', 'label': 'Receptions' }, - 42: { 'abbr': 'REY', 'label': 'Receiving Yards' }, - 43: { 'abbr': 'RETD', 'label': 'TD Reception' }, - 44: { 'abbr': '2PRE', 'label': '2pt Receiving Conversion' }, - 45: { 'abbr': 'RETD40', 'label': '40+ yard TD rec bonus' }, - 46: { 'abbr': 'RETD50', 'label': '50+ yard TD rec bonus' }, - 47: { 'abbr': 'REY5', 'label': 'Every 5 receiving yards' }, - 48: { 'abbr': 'REY10', 'label': 'Every 10 receiving yards' }, - 49: { 'abbr': 'REY20', 'label': 'Every 20 receiving yards' }, - 50: { 'abbr': 'REY25', 'label': 'Every 25 receiving yards' }, - 51: { 'abbr': 'REY50', 'label': 'Every 50 receiving yards' }, - 52: { 'abbr': 'RE100', 'label': 'Every 100 receiving yards' }, - 53: { 'abbr': 'REC', 'label': 'Each reception' }, - 54: { 'abbr': 'REC5', 'label': 'Every 5 receptions'}, - 55: { 'abbr': 'REC10', 'label': 'Every 10 receptions' }, - 56: { 'abbr': 'REY100', 'label': '100-199 yard receiving game' }, - 57: { 'abbr': 'REY200', 'label': '200+ yard receiving game' }, - 58: { 'abbr': 'RET', 'label': 'Receiving Target' }, - 59: { 'abbr': 'YAC', 'label': 'Receiving Yards After Catch' }, - 60: { 'abbr': 'YPC', 'label': 'Receiving Yards Per Catch' }, - 61: { 'abbr': 'REYPG', 'label': 'Receiving Yards Per Game' }, - 62: { 'abbr': 'PTL', 'label': 'Total 2pt Conversions' }, - 63: { 'abbr': 'FTD', 'label': 'Fumble Recovered for TD' }, - 64: { 'abbr': 'SKD', 'label': 'Sacked' }, - 65: { 'abbr': 'PFUM', 'label': 'Passing Fumbles' }, - 66: { 'abbr': 'RFUM', 'label': 'Rushing Fumbles' }, - 67: { 'abbr': 'REFUM', 'label': 'Receiving Fumbles' }, - 68: { 'abbr': 'FUM', 'label': 'Total Fumbles' }, - 69: { 'abbr': 'PFUML', 'label': 'Passing Fumbles Lost' }, - 70: { 'abbr': 'RFUML', 'label': 'Rushing Fumbles Lost' }, - 71: { 'abbr': 'REFUML', 'label': 'Receiving Fumbles Lost' }, - 72: { 'abbr': 'FUML', 'label': 'Total Fumbles Lost' }, - 73: { 'abbr': 'TT', 'label': 'Total Turnovers' }, - 74: { 'abbr': 'FG50P', 'label': 'FG Made (50+ yards)' }, - 75: { 'abbr': 'FGA50P', 'label': 'FG Attempted (50+ yards)' }, - 76: { 'abbr': 'FGM50P', 'label': 'FG Missed (50+ yards)' }, - 77: { 'abbr': 'FG40', 'label': 'FG Made (40-49 yards)' }, - 78: { 'abbr': 'FGA40', 'label': 'FG Attempted (40-49 yards)' }, - 79: { 'abbr': 'FGM40', 'label': 'FG Missed (40-49 yards)' }, - 80: { 'abbr': 'FG0', 'label': 'FG Made (0-39 yards)' }, - 81: { 'abbr': 'FGA0', 'label': 'FG Attempted (0-39 yards)' }, - 82: { 'abbr': 'FGM0', 'label': 'FG Missed (0-39 yards)' }, - 83: { 'abbr': 'FG', 'label': 'Total FG Made' }, - 84: { 'abbr': 'FGA', 'label': 'Total FG Attempted' }, - 85: { 'abbr': 'FGM', 'label': 'Total FG Missed' }, - 86: { 'abbr': 'PAT', 'label': 'Each PAT Made' }, - 87: { 'abbr': 'PATA', 'label': 'Each PAT Attempted' }, - 88: { 'abbr': 'PATM', 'label': 'Each PAT Missed' }, - 89: { 'abbr': 'PA0', 'label': '0 points allowed' }, - 90: { 'abbr': 'PA1', 'label': '1-6 points allowed' }, - 91: { 'abbr': 'PA7', 'label': '7-13 points allowed' }, - 92: { 'abbr': 'PA14', 'label': '14-17 points allowed' }, - 93: { 'abbr': 'BLKKRTD', 'label': 'Blocked Punt or FG return for TD' }, - 94: { 'abbr': 'DEFRETTD', 'label': 'Fumble or INT Return for TD' }, - 95: { 'abbr': 'INT', 'label': 'Each Interception' }, - 96: { 'abbr': 'FR', 'label': 'Each Fumble Recovered' }, - 97: { 'abbr': 'BLKK', 'label': 'Blocked Punt, PAT or FG' }, - 98: { 'abbr': 'SF', 'label': 'Each Safety' }, - 99: { 'abbr': 'SK', 'label': 'Each Sack' }, - 100: { 'abbr': 'HALFSK', 'label': '1/2 Sack' }, - 101: { 'abbr': 'KRTD', 'label': 'Kickoff Return TD' }, - 102: { 'abbr': 'PRTD', 'label': 'Punt Return TD' }, - 103: { 'abbr': 'INTTD', 'label': 'Interception Return TD' }, - 104: { 'abbr': 'FRTD', 'label': 'Fumble Return TD' }, - 105: { 'abbr': 'TRTD', 'label': 'Total Return TD' }, - 106: { 'abbr': 'FF', 'label': 'Each Fumble Forced' }, - 107: { 'abbr': 'TKA', 'label': 'Assisted Tackles' }, - 108: { 'abbr': 'TKS', 'label': 'Solo Tackles' }, - 109: { 'abbr': 'TK', 'label': 'Total Tackles' }, - 110: { 'abbr': 'TK3', 'label': 'Every 3 Total Tackles' }, - 111: { 'abbr': 'TK5', 'label': 'Every 5 Total Tackles' }, - 112: { 'abbr': 'STF', 'label': 'Stuffs' }, - 113: { 'abbr': 'PD', 'label': 'Passes Defensed' }, - 114: { 'abbr': 'KR', 'label': 'Kickoff Return Yards' }, - 115: { 'abbr': 'PR', 'label': 'Punt Return Yards' }, - 116: { 'abbr': 'KR10', 'label': 'Every 10 kickoff return yards' }, - 117: { 'abbr': 'KR25', 'label': 'Every 25 kickoff return yards' }, - 118: { 'abbr': 'PR10', 'label': 'Every 10 punt return yards' }, - 119: { 'abbr': 'PR25', 'label': 'Every 25 punt return yards' }, - 120: { 'abbr': 'PTSA', 'label': 'Points Allowed' }, - 121: { 'abbr': 'PA18', 'label': '18-21 points allowed' }, - 122: { 'abbr': 'PA22', 'label': '22-27 points allowed' }, - 123: { 'abbr': 'PA28', 'label': '28-34 points allowed' }, - 124: { 'abbr': 'PA35', 'label': '35-45 points allowed' }, - 125: { 'abbr': 'PA46', 'label': '46+ points allowed' }, - 126: { 'abbr': 'PAPG', 'label': 'Points Allowed Per Game' }, - 127: { 'abbr': 'YA', 'label': 'Yards Allowed' }, - 128: { 'abbr': 'YA100', 'label': 'Less than 100 total yards allowed' }, - 129: { 'abbr': 'YA199', 'label': '100-199 total yards allowed' }, - 130: { 'abbr': 'YA299', 'label': '200-299 total yards allowed' }, - 131: { 'abbr': 'YA349', 'label': '300-349 total yards allowed' }, - 132: { 'abbr': 'YA399', 'label': '350-399 total yards allowed' }, - 133: { 'abbr': 'YA449', 'label': '400-449 total yards allowed' }, - 134: { 'abbr': 'YA499', 'label': '450-499 total yards allowed' }, - 135: { 'abbr': 'YA549', 'label': '500-549 total yards allowed' }, - 136: { 'abbr': 'YA550', 'label': '550+ total yards allowed' }, - 137: { 'abbr': 'YAPG', 'label': 'Yards Allowed Per Game' }, - 138: { 'abbr': 'PT', 'label': 'Net Punts' }, - 139: { 'abbr': 'PTY', 'label': 'Punt Yards' }, - 140: { 'abbr': 'PT10', 'label': 'Punts Inside the 10' }, - 141: { 'abbr': 'PT20', 'label': 'Punts Inside the 20' }, - 142: { 'abbr': 'PTB', 'label': 'Blocked Punts' }, - 143: { 'abbr': 'PTR', 'label': 'Punts Returned' }, - 144: { 'abbr': 'PTRY', 'label': 'Punt Return Yards' }, - 145: { 'abbr': 'PTTB', 'label': 'Touchbacks' }, - 146: { 'abbr': 'PTFC', 'label': 'Fair Catches' }, - 147: { 'abbr': 'PTAVG', 'label': 'Punt Average' }, - 148: { 'abbr': 'PTA44', 'label': 'Punt Average 44.0+' }, - 149: { 'abbr': 'PTA42', 'label': 'Punt Average 42.0-43.9' }, - 150: { 'abbr': 'PTA40', 'label': 'Punt Average 40.0-41.9' }, - 151: { 'abbr': 'PTA38', 'label': 'Punt Average 38.0-39.9' }, - 152: { 'abbr': 'PTA36', 'label': 'Punt Average 36.0-37.9' }, - 153: { 'abbr': 'PTA34', 'label': 'Punt Average 34.0-35.9' }, - 154: { 'abbr': 'PTA33', 'label': 'Punt Average 33.9 or less' }, - 155: { 'abbr': 'TW', 'label': 'Team Win' }, - 156: { 'abbr': 'TL', 'label': 'Team Loss' }, - 157: { 'abbr': 'TIE', 'label': 'Team Tie' }, - 158: { 'abbr': 'PTS', 'label': 'Points Scored' }, - 159: { 'abbr': 'PPG', 'label': 'Points Scored Per Game' }, - 160: { 'abbr': 'MGN', 'label': 'Margin of Victory' }, - 161: { 'abbr': 'WM25', 'label': '25+ point Win Margin' }, - 162: { 'abbr': 'WM20', 'label': '20-24 point Win Margin' }, - 163: { 'abbr': 'WM15', 'label': '15-19 point Win Margin' }, - 164: { 'abbr': 'WM10', 'label': '10-14 point Win Margin' }, - 165: { 'abbr': 'WM5', 'label': '5-9 point Win Margin' }, - 166: { 'abbr': 'WM1', 'label': '1-4 point Win Margin' }, - 167: { 'abbr': 'LM1', 'label': '1-4 point Loss Margin' }, - 168: { 'abbr': 'LM5', 'label': '5-9 point Loss Margin' }, - 169: { 'abbr': 'LM10', 'label': '10-14 point Loss Margin' }, - 170: { 'abbr': 'LM15', 'label': '15-19 point Loss Margin' }, - 171: { 'abbr': 'LM20', 'label': '20-24 point Loss Margin' }, - 172: { 'abbr': 'LM25', 'label': '25+ point Loss Margin' }, - 173: { 'abbr': 'MGNPG', 'label': 'Margin of Victory Per Game' }, - 174: { 'abbr': 'WINPCT', 'label': 'Winning Pct' }, - 175: { 'abbr': 'PTD0', 'label': '0-9 yd TD pass bonus' }, - 176: { 'abbr': 'PTD10', 'label': '10-19 yd TD pass bonus' }, - 177: { 'abbr': 'PTD20', 'label': '20-29 yd TD pass bonus' }, - 178: { 'abbr': 'PTD30', 'label': '30-39 yd TD pass bonus' }, - 179: { 'abbr': 'RTD0', 'label': '0-9 yd TD rush bonus' }, - 180: { 'abbr': 'RTD10', 'label': '10-19 yd TD rush bonus' }, - 181: { 'abbr': 'RTD20', 'label': '20-29 yd TD rush bonus' }, - 182: { 'abbr': 'RTD30', 'label': '30-39 yd TD rush bonus' }, - 183: { 'abbr': 'RETD0', 'label': '0-9 yd TD rec bonus' }, - 184: { 'abbr': 'RETD10', 'label': '10-19 yd TD rec bonus' }, - 185: { 'abbr': 'RETD20', 'label': '20-29 yd TD rec bonus' }, - 186: { 'abbr': 'RETD30', 'label': '30-39 yd TD rec bonus' }, - 187: { 'abbr': 'DPTSA', 'label': 'D/ST Points Allowed' }, - 188: { 'abbr': 'DPA0', 'label': 'D/ST 0 points allowed' }, - 189: { 'abbr': 'DPA1', 'label': 'D/ST 1-6 points allowed' }, - 190: { 'abbr': 'DPA7', 'label': 'D/ST 7-13 points allowed' }, - 191: { 'abbr': 'DPA14', 'label': 'D/ST 14-17 points allowed' }, - 192: { 'abbr': 'DPA18', 'label': 'D/ST 18-21 points allowed' }, - 193: { 'abbr': 'DPA22', 'label': 'D/ST 22-27 points allowed' }, - 194: { 'abbr': 'DPA28', 'label': 'D/ST 28-34 points allowed' }, - 195: { 'abbr': 'DPA35', 'label': 'D/ST 35-45 points allowed' }, - 196: { 'abbr': 'DPA46', 'label': 'D/ST 46+ points allowed' }, - 197: { 'abbr': 'DPAPG', 'label': 'D/ST Points Allowed Per Game' }, - 198: { 'abbr': 'FG50', 'label': 'FG Made (50-59 yards)' }, - 199: { 'abbr': 'FGA50', 'label': 'FG Attempted (50-59 yards)' }, - 200: { 'abbr': 'FGM50', 'label': 'FG Missed (50-59 yards)' }, - 201: { 'abbr': 'FG60', 'label': 'FG Made (60+ yards)' }, - 202: { 'abbr': 'FGA60', 'label': 'FG Attempted (60+ yards)' }, - 203: { 'abbr': 'FGM60', 'label': 'FG Missed (60+ yards)' }, - 204: { 'abbr': 'O2PRET', 'label': 'Offensive 2pt Return' }, - 205: { 'abbr': 'D2PRET', 'label': 'Defensive 2pt Return' }, - 206: { 'abbr': '2PRET', 'label': '2pt Return' }, - 207: { 'abbr': 'O1PSF', 'label': 'Offensive 1pt Safety' }, - 208: { 'abbr': 'D1PSF', 'label': 'Defensive 1pt Safety' }, - 209: { 'abbr': '1PSF', 'label': '1pt Safety' }, - 210: { 'abbr': 'GP', 'label': 'Games Played' }, - 211: { 'abbr': 'PFD', 'label': 'Passing First Down' }, - 212: { 'abbr': 'RFD', 'label': 'Rushing First Down' }, - 213: { 'abbr': 'REFD', 'label': 'Receiving First Down' }, - 214: { 'abbr': 'FGY', 'label': 'FG Made Yards' }, - 215: { 'abbr': 'FGMY', 'label': 'FG Missed Yards' }, - 216: { 'abbr': 'FGAY', 'label': 'FG Attempt Yards' }, - 217: { 'abbr': 'FGY5', 'label': 'Every 5 FG Made yards' }, - 218: { 'abbr': 'FGY10', 'label': 'Every 10 FG Made yards' }, - 219: { 'abbr': 'FGY20', 'label': 'Every 20 FG Made yards' }, - 220: { 'abbr': 'FGY25', 'label': 'Every 25 FG Made yards' }, - 221: { 'abbr': 'FGY50', 'label': 'Every 50 FG Made yards' }, - 222: { 'abbr': 'FGY100', 'label': 'Every 100 FG Made yards' }, - 223: { 'abbr': 'FGMY5', 'label': 'Every 5 FG Missed yards' }, - 224: { 'abbr': 'FGMY10', 'label': 'Every 10 FG Missed yards' }, - 225: { 'abbr': 'FGMY20', 'label': 'Every 20 FG Missed yards' }, - 226: { 'abbr': 'FGMY25', 'label': 'Every 25 FG Missed yards' }, - 227: { 'abbr': 'FGMY50', 'label': 'Every 50 FG Missed yards' }, - 228: { 'abbr': 'FGMY100', 'label': 'Every 100 FG Missed yards' }, - 229: { 'abbr': 'FGAY5', 'label': 'Every 5 FG Attempt yards' }, - 230: { 'abbr': 'FGAY10', 'label': 'Every 10 FG Attempt yards' }, - 231: { 'abbr': 'FGAY20', 'label': 'Every 20 FG Attempt yards' }, - 232: { 'abbr': 'FGAY25', 'label': 'Every 25 FG Attempt yards' }, - 233: { 'abbr': 'FGAY50', 'label': 'Every 50 FG Attempt yards' }, - 234: { 'abbr': 'FGAY100', 'label': 'Every 100 FG Attempt yards' } + 0: {"abbr": "PA", "label": "Each Pass Attempted"}, + 1: {"abbr": "PC", "label": "Each Pass Completed"}, + 2: {"abbr": "INC", "label": "Each Incomplete Pass"}, + 3: {"abbr": "PY", "label": "Passing Yards"}, + 4: {"abbr": "PTD", "label": "TD Pass"}, + 5: {"abbr": "PY5", "label": "Every 5 passing yards"}, + 6: {"abbr": "PY10", "label": "Every 10 passing yards"}, + 7: {"abbr": "PY20", "label": "Every 20 passing yards"}, + 8: {"abbr": "PY25", "label": "Every 25 passing yards"}, + 9: {"abbr": "PY50", "label": "Every 50 passing yards"}, + 10: {"abbr": "PY100", "label": "Every 100 passing yards"}, + 11: {"abbr": "PC5", "label": "Every 5 pass completions"}, + 12: {"abbr": "PC10", "label": "Every 10 pass completions"}, + 13: {"abbr": "IP5", "label": "Every 5 pass incompletions"}, + 14: {"abbr": "IP10", "label": "Every 10 pass incompletions"}, + 15: {"abbr": "PTD40", "label": "40+ yard TD pass bonus"}, + 16: {"abbr": "PTD50", "label": "50+ yard TD pass bonus"}, + 17: {"abbr": "P300", "label": "300-399 yard passing game"}, + 18: {"abbr": "P400", "label": "400+ yard passing game"}, + 19: {"abbr": "2PC", "label": "2pt Passing Conversion"}, + 20: {"abbr": "INTT", "label": "Interceptions Thrown"}, + 21: {"abbr": "CPCT", "label": "Passing Completion Pct"}, + 22: {"abbr": "PYPG", "label": "Passing Yards Per Game"}, + 23: {"abbr": "RA", "label": "Rushing Attempts"}, + 24: {"abbr": "RY", "label": "Rushing Yards"}, + 25: {"abbr": "RTD", "label": "TD Rush"}, + 26: {"abbr": "2PR", "label": "2pt Rushing Conversion"}, + 27: {"abbr": "RY5", "label": "Every 5 rushing yards"}, + 28: {"abbr": "RY10", "label": "Every 10 rushing yards"}, + 29: {"abbr": "RY20", "label": "Every 20 rushing yards"}, + 30: {"abbr": "RY25", "label": "Every 25 rushing yards"}, + 31: {"abbr": "RY50", "label": "Every 50 rushing yards"}, + 32: {"abbr": "R100", "label": "Every 100 rushing yards"}, + 33: {"abbr": "RA5", "label": "Every 5 rush attempts"}, + 34: {"abbr": "RA10", "label": "Every 10 rush attempts"}, + 35: {"abbr": "RTD40", "label": "40+ yard TD rush bonus"}, + 36: {"abbr": "RTD50", "label": "50+ yard TD rush bonus"}, + 37: {"abbr": "RY100", "label": "100-199 yard rushing game"}, + 38: {"abbr": "RY200", "label": "200+ yard rushing game"}, + 39: {"abbr": "RYPA", "label": "Rushing Yards Per Attempt"}, + 40: {"abbr": "RYPG", "label": "Rushing Yards Per Game"}, + 41: {"abbr": "RECS", "label": "Receptions"}, + 42: {"abbr": "REY", "label": "Receiving Yards"}, + 43: {"abbr": "RETD", "label": "TD Reception"}, + 44: {"abbr": "2PRE", "label": "2pt Receiving Conversion"}, + 45: {"abbr": "RETD40", "label": "40+ yard TD rec bonus"}, + 46: {"abbr": "RETD50", "label": "50+ yard TD rec bonus"}, + 47: {"abbr": "REY5", "label": "Every 5 receiving yards"}, + 48: {"abbr": "REY10", "label": "Every 10 receiving yards"}, + 49: {"abbr": "REY20", "label": "Every 20 receiving yards"}, + 50: {"abbr": "REY25", "label": "Every 25 receiving yards"}, + 51: {"abbr": "REY50", "label": "Every 50 receiving yards"}, + 52: {"abbr": "RE100", "label": "Every 100 receiving yards"}, + 53: {"abbr": "REC", "label": "Each reception"}, + 54: {"abbr": "REC5", "label": "Every 5 receptions"}, + 55: {"abbr": "REC10", "label": "Every 10 receptions"}, + 56: {"abbr": "REY100", "label": "100-199 yard receiving game"}, + 57: {"abbr": "REY200", "label": "200+ yard receiving game"}, + 58: {"abbr": "RET", "label": "Receiving Target"}, + 59: {"abbr": "YAC", "label": "Receiving Yards After Catch"}, + 60: {"abbr": "YPC", "label": "Receiving Yards Per Catch"}, + 61: {"abbr": "REYPG", "label": "Receiving Yards Per Game"}, + 62: {"abbr": "PTL", "label": "Total 2pt Conversions"}, + 63: {"abbr": "FTD", "label": "Fumble Recovered for TD"}, + 64: {"abbr": "SKD", "label": "Sacked"}, + 65: {"abbr": "PFUM", "label": "Passing Fumbles"}, + 66: {"abbr": "RFUM", "label": "Rushing Fumbles"}, + 67: {"abbr": "REFUM", "label": "Receiving Fumbles"}, + 68: {"abbr": "FUM", "label": "Total Fumbles"}, + 69: {"abbr": "PFUML", "label": "Passing Fumbles Lost"}, + 70: {"abbr": "RFUML", "label": "Rushing Fumbles Lost"}, + 71: {"abbr": "REFUML", "label": "Receiving Fumbles Lost"}, + 72: {"abbr": "FUML", "label": "Total Fumbles Lost"}, + 73: {"abbr": "TT", "label": "Total Turnovers"}, + 74: {"abbr": "FG50P", "label": "FG Made (50+ yards)"}, + 75: {"abbr": "FGA50P", "label": "FG Attempted (50+ yards)"}, + 76: {"abbr": "FGM50P", "label": "FG Missed (50+ yards)"}, + 77: {"abbr": "FG40", "label": "FG Made (40-49 yards)"}, + 78: {"abbr": "FGA40", "label": "FG Attempted (40-49 yards)"}, + 79: {"abbr": "FGM40", "label": "FG Missed (40-49 yards)"}, + 80: {"abbr": "FG0", "label": "FG Made (0-39 yards)"}, + 81: {"abbr": "FGA0", "label": "FG Attempted (0-39 yards)"}, + 82: {"abbr": "FGM0", "label": "FG Missed (0-39 yards)"}, + 83: {"abbr": "FG", "label": "Total FG Made"}, + 84: {"abbr": "FGA", "label": "Total FG Attempted"}, + 85: {"abbr": "FGM", "label": "Total FG Missed"}, + 86: {"abbr": "PAT", "label": "Each PAT Made"}, + 87: {"abbr": "PATA", "label": "Each PAT Attempted"}, + 88: {"abbr": "PATM", "label": "Each PAT Missed"}, + 89: {"abbr": "PA0", "label": "0 points allowed"}, + 90: {"abbr": "PA1", "label": "1-6 points allowed"}, + 91: {"abbr": "PA7", "label": "7-13 points allowed"}, + 92: {"abbr": "PA14", "label": "14-17 points allowed"}, + 93: {"abbr": "BLKKRTD", "label": "Blocked Punt or FG return for TD"}, + 94: {"abbr": "DEFRETTD", "label": "Fumble or INT Return for TD"}, + 95: {"abbr": "INT", "label": "Each Interception"}, + 96: {"abbr": "FR", "label": "Each Fumble Recovered"}, + 97: {"abbr": "BLKK", "label": "Blocked Punt, PAT or FG"}, + 98: {"abbr": "SF", "label": "Each Safety"}, + 99: {"abbr": "SK", "label": "Each Sack"}, + 100: {"abbr": "HALFSK", "label": "1/2 Sack"}, + 101: {"abbr": "KRTD", "label": "Kickoff Return TD"}, + 102: {"abbr": "PRTD", "label": "Punt Return TD"}, + 103: {"abbr": "INTTD", "label": "Interception Return TD"}, + 104: {"abbr": "FRTD", "label": "Fumble Return TD"}, + 105: {"abbr": "TRTD", "label": "Total Return TD"}, + 106: {"abbr": "FF", "label": "Each Fumble Forced"}, + 107: {"abbr": "TKA", "label": "Assisted Tackles"}, + 108: {"abbr": "TKS", "label": "Solo Tackles"}, + 109: {"abbr": "TK", "label": "Total Tackles"}, + 110: {"abbr": "TK3", "label": "Every 3 Total Tackles"}, + 111: {"abbr": "TK5", "label": "Every 5 Total Tackles"}, + 112: {"abbr": "STF", "label": "Stuffs"}, + 113: {"abbr": "PD", "label": "Passes Defensed"}, + 114: {"abbr": "KR", "label": "Kickoff Return Yards"}, + 115: {"abbr": "PR", "label": "Punt Return Yards"}, + 116: {"abbr": "KR10", "label": "Every 10 kickoff return yards"}, + 117: {"abbr": "KR25", "label": "Every 25 kickoff return yards"}, + 118: {"abbr": "PR10", "label": "Every 10 punt return yards"}, + 119: {"abbr": "PR25", "label": "Every 25 punt return yards"}, + 120: {"abbr": "PTSA", "label": "Points Allowed"}, + 121: {"abbr": "PA18", "label": "18-21 points allowed"}, + 122: {"abbr": "PA22", "label": "22-27 points allowed"}, + 123: {"abbr": "PA28", "label": "28-34 points allowed"}, + 124: {"abbr": "PA35", "label": "35-45 points allowed"}, + 125: {"abbr": "PA46", "label": "46+ points allowed"}, + 126: {"abbr": "PAPG", "label": "Points Allowed Per Game"}, + 127: {"abbr": "YA", "label": "Yards Allowed"}, + 128: {"abbr": "YA100", "label": "Less than 100 total yards allowed"}, + 129: {"abbr": "YA199", "label": "100-199 total yards allowed"}, + 130: {"abbr": "YA299", "label": "200-299 total yards allowed"}, + 131: {"abbr": "YA349", "label": "300-349 total yards allowed"}, + 132: {"abbr": "YA399", "label": "350-399 total yards allowed"}, + 133: {"abbr": "YA449", "label": "400-449 total yards allowed"}, + 134: {"abbr": "YA499", "label": "450-499 total yards allowed"}, + 135: {"abbr": "YA549", "label": "500-549 total yards allowed"}, + 136: {"abbr": "YA550", "label": "550+ total yards allowed"}, + 137: {"abbr": "YAPG", "label": "Yards Allowed Per Game"}, + 138: {"abbr": "PT", "label": "Net Punts"}, + 139: {"abbr": "PTY", "label": "Punt Yards"}, + 140: {"abbr": "PT10", "label": "Punts Inside the 10"}, + 141: {"abbr": "PT20", "label": "Punts Inside the 20"}, + 142: {"abbr": "PTB", "label": "Blocked Punts"}, + 143: {"abbr": "PTR", "label": "Punts Returned"}, + 144: {"abbr": "PTRY", "label": "Punt Return Yards"}, + 145: {"abbr": "PTTB", "label": "Touchbacks"}, + 146: {"abbr": "PTFC", "label": "Fair Catches"}, + 147: {"abbr": "PTAVG", "label": "Punt Average"}, + 148: {"abbr": "PTA44", "label": "Punt Average 44.0+"}, + 149: {"abbr": "PTA42", "label": "Punt Average 42.0-43.9"}, + 150: {"abbr": "PTA40", "label": "Punt Average 40.0-41.9"}, + 151: {"abbr": "PTA38", "label": "Punt Average 38.0-39.9"}, + 152: {"abbr": "PTA36", "label": "Punt Average 36.0-37.9"}, + 153: {"abbr": "PTA34", "label": "Punt Average 34.0-35.9"}, + 154: {"abbr": "PTA33", "label": "Punt Average 33.9 or less"}, + 155: {"abbr": "TW", "label": "Team Win"}, + 156: {"abbr": "TL", "label": "Team Loss"}, + 157: {"abbr": "TIE", "label": "Team Tie"}, + 158: {"abbr": "PTS", "label": "Points Scored"}, + 159: {"abbr": "PPG", "label": "Points Scored Per Game"}, + 160: {"abbr": "MGN", "label": "Margin of Victory"}, + 161: {"abbr": "WM25", "label": "25+ point Win Margin"}, + 162: {"abbr": "WM20", "label": "20-24 point Win Margin"}, + 163: {"abbr": "WM15", "label": "15-19 point Win Margin"}, + 164: {"abbr": "WM10", "label": "10-14 point Win Margin"}, + 165: {"abbr": "WM5", "label": "5-9 point Win Margin"}, + 166: {"abbr": "WM1", "label": "1-4 point Win Margin"}, + 167: {"abbr": "LM1", "label": "1-4 point Loss Margin"}, + 168: {"abbr": "LM5", "label": "5-9 point Loss Margin"}, + 169: {"abbr": "LM10", "label": "10-14 point Loss Margin"}, + 170: {"abbr": "LM15", "label": "15-19 point Loss Margin"}, + 171: {"abbr": "LM20", "label": "20-24 point Loss Margin"}, + 172: {"abbr": "LM25", "label": "25+ point Loss Margin"}, + 173: {"abbr": "MGNPG", "label": "Margin of Victory Per Game"}, + 174: {"abbr": "WINPCT", "label": "Winning Pct"}, + 175: {"abbr": "PTD0", "label": "0-9 yd TD pass bonus"}, + 176: {"abbr": "PTD10", "label": "10-19 yd TD pass bonus"}, + 177: {"abbr": "PTD20", "label": "20-29 yd TD pass bonus"}, + 178: {"abbr": "PTD30", "label": "30-39 yd TD pass bonus"}, + 179: {"abbr": "RTD0", "label": "0-9 yd TD rush bonus"}, + 180: {"abbr": "RTD10", "label": "10-19 yd TD rush bonus"}, + 181: {"abbr": "RTD20", "label": "20-29 yd TD rush bonus"}, + 182: {"abbr": "RTD30", "label": "30-39 yd TD rush bonus"}, + 183: {"abbr": "RETD0", "label": "0-9 yd TD rec bonus"}, + 184: {"abbr": "RETD10", "label": "10-19 yd TD rec bonus"}, + 185: {"abbr": "RETD20", "label": "20-29 yd TD rec bonus"}, + 186: {"abbr": "RETD30", "label": "30-39 yd TD rec bonus"}, + 187: {"abbr": "DPTSA", "label": "D/ST Points Allowed"}, + 188: {"abbr": "DPA0", "label": "D/ST 0 points allowed"}, + 189: {"abbr": "DPA1", "label": "D/ST 1-6 points allowed"}, + 190: {"abbr": "DPA7", "label": "D/ST 7-13 points allowed"}, + 191: {"abbr": "DPA14", "label": "D/ST 14-17 points allowed"}, + 192: {"abbr": "DPA18", "label": "D/ST 18-21 points allowed"}, + 193: {"abbr": "DPA22", "label": "D/ST 22-27 points allowed"}, + 194: {"abbr": "DPA28", "label": "D/ST 28-34 points allowed"}, + 195: {"abbr": "DPA35", "label": "D/ST 35-45 points allowed"}, + 196: {"abbr": "DPA46", "label": "D/ST 46+ points allowed"}, + 197: {"abbr": "DPAPG", "label": "D/ST Points Allowed Per Game"}, + 198: {"abbr": "FG50", "label": "FG Made (50-59 yards)"}, + 199: {"abbr": "FGA50", "label": "FG Attempted (50-59 yards)"}, + 200: {"abbr": "FGM50", "label": "FG Missed (50-59 yards)"}, + 201: {"abbr": "FG60", "label": "FG Made (60+ yards)"}, + 202: {"abbr": "FGA60", "label": "FG Attempted (60+ yards)"}, + 203: {"abbr": "FGM60", "label": "FG Missed (60+ yards)"}, + 204: {"abbr": "O2PRET", "label": "Offensive 2pt Return"}, + 205: {"abbr": "D2PRET", "label": "Defensive 2pt Return"}, + 206: {"abbr": "2PRET", "label": "2pt Return"}, + 207: {"abbr": "O1PSF", "label": "Offensive 1pt Safety"}, + 208: {"abbr": "D1PSF", "label": "Defensive 1pt Safety"}, + 209: {"abbr": "1PSF", "label": "1pt Safety"}, + 210: {"abbr": "GP", "label": "Games Played"}, + 211: {"abbr": "PFD", "label": "Passing First Down"}, + 212: {"abbr": "RFD", "label": "Rushing First Down"}, + 213: {"abbr": "REFD", "label": "Receiving First Down"}, + 214: {"abbr": "FGY", "label": "FG Made Yards"}, + 215: {"abbr": "FGMY", "label": "FG Missed Yards"}, + 216: {"abbr": "FGAY", "label": "FG Attempt Yards"}, + 217: {"abbr": "FGY5", "label": "Every 5 FG Made yards"}, + 218: {"abbr": "FGY10", "label": "Every 10 FG Made yards"}, + 219: {"abbr": "FGY20", "label": "Every 20 FG Made yards"}, + 220: {"abbr": "FGY25", "label": "Every 25 FG Made yards"}, + 221: {"abbr": "FGY50", "label": "Every 50 FG Made yards"}, + 222: {"abbr": "FGY100", "label": "Every 100 FG Made yards"}, + 223: {"abbr": "FGMY5", "label": "Every 5 FG Missed yards"}, + 224: {"abbr": "FGMY10", "label": "Every 10 FG Missed yards"}, + 225: {"abbr": "FGMY20", "label": "Every 20 FG Missed yards"}, + 226: {"abbr": "FGMY25", "label": "Every 25 FG Missed yards"}, + 227: {"abbr": "FGMY50", "label": "Every 50 FG Missed yards"}, + 228: {"abbr": "FGMY100", "label": "Every 100 FG Missed yards"}, + 229: {"abbr": "FGAY5", "label": "Every 5 FG Attempt yards"}, + 230: {"abbr": "FGAY10", "label": "Every 10 FG Attempt yards"}, + 231: {"abbr": "FGAY20", "label": "Every 20 FG Attempt yards"}, + 232: {"abbr": "FGAY25", "label": "Every 25 FG Attempt yards"}, + 233: {"abbr": "FGAY50", "label": "Every 50 FG Attempt yards"}, + 234: {"abbr": "FGAY100", "label": "Every 100 FG Attempt yards"}, } TRANSACTION_TYPES = { - 'DRAFT', - 'TRADE_ACCEPT', - 'WAIVER', - 'TRADE_VETO', - 'FUTURE_ROSTER', - 'ROSTER', - 'RETRO_ROSTER', - 'TRADE_PROPOSAL', - 'TRADE_UPHOLD', - 'FREEAGENT', - 'TRADE_DECLINE', - 'WAIVER_ERROR', - 'TRADE_ERROR' + "DRAFT", + "TRADE_ACCEPT", + "WAIVER", + "TRADE_VETO", + "FUTURE_ROSTER", + "ROSTER", + "RETRO_ROSTER", + "TRADE_PROPOSAL", + "TRADE_UPHOLD", + "FREEAGENT", + "TRADE_DECLINE", + "WAIVER_ERROR", + "TRADE_ERROR", } diff --git a/espn_api/football/league.py b/espn_api/football/league.py index 4f4b964e2..e0193d78b 100644 --- a/espn_api/football/league.py +++ b/espn_api/football/league.py @@ -26,9 +26,25 @@ class League(BaseLeague): - '''Creates a League instance for Public/Private ESPN league''' - def __init__(self, league_id: int, year: int, espn_s2=None, swid=None, fetch_league=True, debug=False): - super().__init__(league_id=league_id, year=year, sport='nfl', espn_s2=espn_s2, swid=swid, debug=debug) + """Creates a League instance for Public/Private ESPN league""" + + def __init__( + self, + league_id: int, + year: int, + espn_s2=None, + swid=None, + fetch_league=True, + debug=False, + ): + super().__init__( + league_id=league_id, + year=year, + sport="nfl", + espn_s2=espn_s2, + swid=swid, + debug=debug, + ) if fetch_league: self.fetch_league() @@ -39,19 +55,19 @@ def fetch_league(self): def _fetch_league(self): data = super()._fetch_league(SettingsClass=Settings) - self.nfl_week = data['status']['latestScoringPeriod'] + self.nfl_week = data["status"]["latestScoringPeriod"] self._fetch_players() self._fetch_teams(data) super()._fetch_draft() def _fetch_teams(self, data): - '''Fetch teams in league''' + """Fetch teams in league""" pro_schedule = self._get_all_pro_schedule() super()._fetch_teams(data, TeamClass=Team, pro_schedule=pro_schedule) # replace opponentIds in schedule with team instances for team in self.teams: - team.division_name = self.settings.division_map.get(team.division_id, '') + team.division_name = self.settings.division_map.get(team.division_id, "") for week, matchup in enumerate(team.schedule): for opponent in self.teams: if matchup == opponent.team_id: @@ -65,25 +81,25 @@ def _fetch_teams(self, data): def _get_positional_ratings(self, week: int): params = { - 'view': 'mPositionalRatings', - 'scoringPeriodId': week, + "view": "mPositionalRatings", + "scoringPeriodId": week, } data = self.espn_request.league_get(params=params) - ratings = data.get('positionAgainstOpponent', {}).get('positionalRatings', {}) + ratings = data.get("positionAgainstOpponent", {}).get("positionalRatings", {}) positional_ratings = {} for pos, rating in ratings.items(): teams_rating = {} - for team, data in rating['ratingsByOpponent'].items(): - teams_rating[team] = data['rank'] + for team, data in rating["ratingsByOpponent"].items(): + teams_rating[team] = data["rank"] positional_ratings[pos] = teams_rating return positional_ratings def refresh(self): - '''Gets latest league data. This can be used instead of creating a new League class each week''' + """Gets latest league data. This can be used instead of creating a new League class each week""" data = super()._fetch_league() - self.nfl_week = data['status']['latestScoringPeriod'] + self.nfl_week = data["status"]["latestScoringPeriod"] self._fetch_teams(data) def refresh_draft(self, refresh_players=False, refresh_teams=False): @@ -95,23 +111,24 @@ def refresh_draft(self, refresh_players=False, refresh_teams=False): self._fetch_teams(data) def load_roster_week(self, week: int) -> None: - '''Sets Teams Roster for a Certain Week''' - params = { - 'view': 'mRoster', - 'scoringPeriodId': week - } + """Sets Teams Roster for a Certain Week""" + params = {"view": "mRoster", "scoringPeriodId": week} data = self.espn_request.league_get(params=params) team_roster = {} - for team in data['teams']: - team_roster[team['id']] = team['roster'] + for team in data["teams"]: + team_roster[team["id"]] = team["roster"] for team in self.teams: roster = team_roster[team.team_id] team._fetch_roster(roster, self.year) def standings(self) -> List[Team]: - standings = sorted(self.teams, key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, reverse=False) + standings = sorted( + self.teams, + key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, + reverse=False, + ) return standings def standings_weekly(self, week: int) -> List[Team]: @@ -151,7 +168,11 @@ def standings_weekly(self, week: int) -> List[Team]: ), "points_for": sum(team.scores[:week]), "points_against": sum( - [team.schedule[w].scores[w] for w in range(week) if team.schedule[w] != team] + [ + team.schedule[w].scores[w] + for w in range(week) + if team.schedule[w] != team + ] ), "schedule": team.schedule[:week], "outcomes": team.outcomes[:week], @@ -238,7 +259,7 @@ def most_points_against(self) -> Team: def top_scored_week(self) -> Tuple[Team, int]: top_week_points = [] for team in self.teams: - top_week_points.append(max(team.scores[:self.current_week])) + top_week_points.append(max(team.scores[: self.current_week])) top_scored_tup = [(i, j) for (i, j) in zip(self.teams, top_week_points)] top_tup = sorted(top_scored_tup, key=lambda tup: float(tup[1]), reverse=True) return top_tup[0] @@ -246,44 +267,64 @@ def top_scored_week(self) -> Tuple[Team, int]: def least_scored_week(self) -> Tuple[Team, int]: least_week_points = [] for team in self.teams: - least_week_points.append(min(team.scores[:self.current_week])) + least_week_points.append(min(team.scores[: self.current_week])) least_scored_tup = [(i, j) for (i, j) in zip(self.teams, least_week_points)] - least_tup = sorted(least_scored_tup, key=lambda tup: float(tup[1]), reverse=False) + least_tup = sorted( + least_scored_tup, key=lambda tup: float(tup[1]), reverse=False + ) return least_tup[0] - - def recent_activity(self, size: int = 25, msg_type: str = None, offset: int = 0) -> List[Activity]: - '''Returns a list of recent league activities (Add, Drop, Trade)''' + def recent_activity( + self, size: int = 25, msg_type: str = None, offset: int = 0 + ) -> List[Activity]: + """Returns a list of recent league activities (Add, Drop, Trade)""" if self.year < 2019: - raise Exception('Cant use recent activity before 2019') + raise Exception("Cant use recent activity before 2019") - msg_types = [178,180,179,239,181,244] + msg_types = [178, 180, 179, 239, 181, 244] if msg_type in ACTIVITY_MAP: msg_types = [ACTIVITY_MAP[msg_type]] - params = { - 'view': 'kona_league_communication' + params = {"view": "kona_league_communication"} + + filters = { + "topics": { + "filterType": {"value": ["ACTIVITY_TRANSACTIONS"]}, + "limit": size, + "limitPerMessageSet": {"value": 25}, + "offset": offset, + "sortMessageDate": {"sortPriority": 1, "sortAsc": False}, + "sortFor": {"sortPriority": 2, "sortAsc": False}, + "filterIncludeMessageTypeIds": {"value": msg_types}, + } } - - filters = {"topics":{"filterType":{"value":["ACTIVITY_TRANSACTIONS"]},"limit":size,"limitPerMessageSet":{"value":25},"offset":offset,"sortMessageDate":{"sortPriority":1,"sortAsc":False},"sortFor":{"sortPriority":2,"sortAsc":False},"filterIncludeMessageTypeIds":{"value":msg_types}}} - headers = {'x-fantasy-filter': json.dumps(filters)} - data = self.espn_request.league_get(extend='/communication/', params=params, headers=headers) - data = data['topics'] - activity = [Activity(topic, self.player_map, self.get_team_data, self.player_info) for topic in data] + headers = {"x-fantasy-filter": json.dumps(filters)} + data = self.espn_request.league_get( + extend="/communication/", params=params, headers=headers + ) + data = data["topics"] + activity = [ + Activity(topic, self.player_map, self.get_team_data, self.player_info) + for topic in data + ] return activity def scoreboard(self, week: int = None) -> List[Matchup]: - '''Returns list of matchups for a given week''' + """Returns list of matchups for a given week""" if not week: week = self.current_week params = { - 'view': 'mMatchupScore', + "view": "mMatchupScore", } data = self.espn_request.league_get(params=params) - schedule = data['schedule'] - matchups = [Matchup(matchup) for matchup in schedule if matchup['matchupPeriodId'] == week] + schedule = data["schedule"] + matchups = [ + Matchup(matchup) + for matchup in schedule + if matchup["matchupPeriodId"] == week + ] for team in self.teams: for matchup in matchups: @@ -294,38 +335,50 @@ def scoreboard(self, week: int = None) -> List[Matchup]: return matchups - def box_scores(self, week: int = None, player_team_cache: dict = None) -> List[BoxScore]: - '''Returns list of box score for a given week\n + def box_scores( + self, week: int = None, player_team_cache: dict = None + ) -> List[BoxScore]: + """Returns list of box score for a given week\n Should only be used with most recent season\n player_team_cache: optional dict mapping playerId -> proTeamId, used to resolve the correct team for players on bye weeks (especially mid-season trades). Mutated in place as new data is encountered, so callers iterating over multiple weeks should - pass the same dict each time.''' + pass the same dict each time.""" if self.year < 2019: - raise Exception('Cant use box score before 2019') + raise Exception("Cant use box score before 2019") matchup_period = self.currentMatchupPeriod scoring_period = self.current_week if week and week <= self.current_week: scoring_period = week for matchup_id in self.settings.matchup_periods: - if week in self.settings.matchup_periods[matchup_id]: - matchup_period = matchup_id - break + if week in self.settings.matchup_periods[matchup_id]: + matchup_period = matchup_id + break params = { - 'view': ['mMatchupScore', 'mScoreboard'], - 'scoringPeriodId': scoring_period, + "view": ["mMatchupScore", "mScoreboard"], + "scoringPeriodId": scoring_period, } - filters = {"schedule":{"filterMatchupPeriodIds":{"value":[matchup_period]}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = {"schedule": {"filterMatchupPeriodIds": {"value": [matchup_period]}}} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - schedule = data['schedule'] + schedule = data["schedule"] pro_schedule = self._get_pro_schedule(scoring_period) positional_rankings = self._get_positional_ratings(scoring_period) - box_data = [BoxScore(matchup, pro_schedule, positional_rankings, scoring_period, self.year, player_team_cache) for matchup in schedule] + box_data = [ + BoxScore( + matchup, + pro_schedule, + positional_rankings, + scoring_period, + self.year, + player_team_cache, + ) + for matchup in schedule + ] for team in self.teams: for matchup in box_data: @@ -335,18 +388,17 @@ def box_scores(self, week: int = None, player_team_cache: dict = None) -> List[B matchup.away_team = team return box_data - def power_rankings(self, week: int=None): - '''Return power rankings for any week''' + def power_rankings(self, week: int = None): + """Return power rankings for any week""" if not week or week <= 0 or week > self.current_week: week = self.current_week # calculate win for every week win_matrix = [] - teams_sorted = sorted(self.teams, key=lambda x: x.team_id, - reverse=False) + teams_sorted = sorted(self.teams, key=lambda x: x.team_id, reverse=False) for team in teams_sorted: - wins = [0]*len(teams_sorted) + wins = [0] * len(teams_sorted) for mov, opponent in zip(team.mov[:week], team.schedule[:week]): opp = teams_sorted.index(opponent) if mov > 0: @@ -356,12 +408,18 @@ def power_rankings(self, week: int=None): power_rank = power_points(dominance_matrix, teams_sorted, week) return power_rank - def free_agents(self, week: int=None, size: int=50, position: str=None, position_id: int=None) -> List[Player]: - '''Returns a List of Free Agents for a Given Week\n - Should only be used with most recent season''' + def free_agents( + self, + week: int = None, + size: int = 50, + position: str = None, + position_id: int = None, + ) -> List[Player]: + """Returns a List of Free Agents for a Given Week\n + Should only be used with most recent season""" if self.year < 2019: - raise Exception('Cant use free agents before 2019') + raise Exception("Cant use free agents before 2019") if not week: week = self.current_week @@ -371,24 +429,40 @@ def free_agents(self, week: int=None, size: int=50, position: str=None, position if position_id: slot_filter.append(position_id) - params = { - 'view': 'kona_player_info', - 'scoringPeriodId': week, + "view": "kona_player_info", + "scoringPeriodId": week, + } + filters = { + "players": { + "filterStatus": {"value": ["FREEAGENT", "WAIVERS"]}, + "filterSlotIds": {"value": slot_filter}, + "limit": size, + "sortPercOwned": {"sortPriority": 1, "sortAsc": False}, + "sortDraftRanks": { + "sortPriority": 100, + "sortAsc": True, + "value": "STANDARD", + }, + } } - filters = {"players":{"filterStatus":{"value":["FREEAGENT","WAIVERS"]},"filterSlotIds":{"value":slot_filter},"limit":size,"sortPercOwned":{"sortPriority":1,"sortAsc":False},"sortDraftRanks":{"sortPriority":100,"sortAsc":True,"value":"STANDARD"}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - players = data['players'] + players = data["players"] pro_schedule = self._get_pro_schedule(week) positional_rankings = self._get_positional_ratings(week) - return [BoxPlayer(player, pro_schedule, positional_rankings, week, self.year) for player in players] + return [ + BoxPlayer(player, pro_schedule, positional_rankings, week, self.year) + for player in players + ] - def player_info(self, name: str = None, playerId: Union[int, list] = None) -> Union[Player, List[Player]]: - ''' Returns Player class if name found ''' + def player_info( + self, name: str = None, playerId: Union[int, list] = None + ) -> Union[Player, List[Player]]: + """Returns Player class if name found""" if name: playerId = self.player_map.get(name) @@ -399,63 +473,75 @@ def player_info(self, name: str = None, playerId: Union[int, list] = None) -> Un data = self.espn_request.get_player_card(playerId, self.finalScoringPeriod) pro_schedule = self._get_all_pro_schedule() - if len(data['players']) == 1: - return Player(data['players'][0], self.year, pro_schedule) - if len(data['players']) > 1: - return [Player(player, self.year, pro_schedule) for player in data['players']] + if len(data["players"]) == 1: + return Player(data["players"][0], self.year, pro_schedule) + if len(data["players"]) > 1: + return [ + Player(player, self.year, pro_schedule) for player in data["players"] + ] def message_board(self, msg_types: List[str] = None): - ''' Returns a list of league messages''' + """Returns a list of league messages""" data = self.espn_request.get_league_message_board(msg_types) - msg_topics = list(data.get('topicsByType', {}).keys()) + msg_topics = list(data.get("topicsByType", {}).keys()) messages = [] for topic in msg_topics: - msgs = data['topicsByType'][topic] + msgs = data["topicsByType"][topic] for msg in msgs: messages.append(msg) return messages - def transactions(self, scoring_period: int = None, types: Set[str] = {"FREEAGENT","WAIVER","WAIVER_ERROR"}) -> List[Transaction]: - '''Returns a list of recent transactions''' + def transactions( + self, + scoring_period: int = None, + types: Set[str] = {"FREEAGENT", "WAIVER", "WAIVER_ERROR"}, + ) -> List[Transaction]: + """Returns a list of recent transactions""" if not scoring_period: scoring_period = self.scoringPeriodId if types > TRANSACTION_TYPES: - raise Exception('Invalid transaction type') + raise Exception("Invalid transaction type") params = { - 'view': 'mTransactions2', - 'scoringPeriodId': scoring_period, + "view": "mTransactions2", + "scoringPeriodId": scoring_period, } - filters = {"transactions":{"filterType":{"value":list(types)}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = {"transactions": {"filterType": {"value": list(types)}}} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - if 'transactions' not in data: - raise Exception('No transactions found') - transactions = data['transactions'] + if "transactions" not in data: + raise Exception("No transactions found") + transactions = data["transactions"] - return [Transaction(transaction, self.player_map, self.get_team_data) for transaction in transactions] + return [ + Transaction(transaction, self.player_map, self.get_team_data) + for transaction in transactions + ] def offers_report(self, week: int = None) -> List[Offer]: - '''Returns a list of all waiver/free agent auction offers sorted by timestamp and bid amount''' + """Returns a list of all waiver/free agent auction offers sorted by timestamp and bid amount""" data = self._get_offers(week) bids = [Offer(bid, self.player_map, self.get_team_data) for bid in data] - + if len(bids) == 0: return [] # Process bids to fix missing timestamps - # For any bid without a timestamp, search for another bid for the same player + # For any bid without a timestamp, search for another bid for the same player # from the same team that has a timestamp and use that timestamp for bid in bids: - if bid.result != 'Canceled' and bid.dateTime is None: + if bid.result != "Canceled" and bid.dateTime is None: for other_bid in bids: if bid.id != other_bid.id and other_bid.dateTime is not None: - if other_bid.player == bid.player and other_bid.teamId == bid.teamId: + if ( + other_bid.player == bid.player + and other_bid.teamId == bid.teamId + ): bid.dateTime = other_bid.dateTime break @@ -465,17 +551,17 @@ def offers_report(self, week: int = None) -> List[Offer]: if bid.dateTime not in reports: reports[bid.dateTime] = [] reports[bid.dateTime].append(bid) - + # Sort report times sorted_report_times = sorted([t for t in reports.keys() if t is not None]) - + # Create a sorted list of offers, with the highest bid for each player first sorted_offers = [] for report_time in sorted_report_times: report = reports[report_time] # Sort by bid amount (highest first) report.sort(reverse=True) - + # Process the bids in order processed_players = set() for bid in report: @@ -483,11 +569,10 @@ def offers_report(self, week: int = None) -> List[Offer]: if bid.player not in processed_players: sorted_offers.append(bid) processed_players.add(bid.player) - + # Find and add all other bids for the same player for other_bid in report: if other_bid.id != bid.id and other_bid.player == bid.player: sorted_offers.append(other_bid) return sorted_offers - diff --git a/espn_api/football/matchup.py b/espn_api/football/matchup.py index c808f5d30..0f6ebae28 100644 --- a/espn_api/football/matchup.py +++ b/espn_api/football/matchup.py @@ -1,27 +1,28 @@ from .team import Team + class Matchup(object): - '''Creates Matchup instance''' + """Creates Matchup instance""" + def __init__(self, data): - self.matchup_type = data.get('playoffTierType', 'NONE') - self.is_playoff = self.matchup_type != 'NONE' - (self._home_team_id, self.home_score) = self._fetch_matchup_info(data, 'home') - (self._away_team_id, self.away_score) = self._fetch_matchup_info(data, 'away') + self.matchup_type = data.get("playoffTierType", "NONE") + self.is_playoff = self.matchup_type != "NONE" + self._home_team_id, self.home_score = self._fetch_matchup_info(data, "home") + self._away_team_id, self.away_score = self._fetch_matchup_info(data, "away") self.home_team: Team self.away_team: Team def __repr__(self): - if hasattr(self, 'away_team'): - return f'Matchup({self.home_team}, {self.away_team})' + if hasattr(self, "away_team"): + return f"Matchup({self.home_team}, {self.away_team})" else: - return f'Matchup({self.home_team}, N/A)' - + return f"Matchup({self.home_team}, N/A)" def _fetch_matchup_info(self, data, team): - '''Fetch info for matchup''' + """Fetch info for matchup""" if team not in data: return (0, 0) - team_id = data[team]['teamId'] - team_score = data[team]['totalPoints'] + team_id = data[team]["teamId"] + team_score = data[team]["totalPoints"] return (team_id, team_score) diff --git a/espn_api/football/player.py b/espn_api/football/player.py index 43c9019f3..64b39bdb9 100644 --- a/espn_api/football/player.py +++ b/espn_api/football/player.py @@ -2,78 +2,115 @@ from .utils import json_parsing from datetime import datetime + class Player(object): - '''Player are part of team''' - def __init__(self, data, year, pro_team_schedule = None): - self.name = json_parsing(data, 'fullName') - self.playerId = json_parsing(data, 'id') - self.posRank = json_parsing(data, 'positionalRanking') - self.eligibleSlots = [POSITION_MAP[pos] for pos in json_parsing(data, 'eligibleSlots')] - self.acquisitionType = json_parsing(data, 'acquisitionType') - self.proTeam = PRO_TEAM_MAP[json_parsing(data, 'proTeamId')] - self.jersey = json_parsing(data, 'jersey') - self.injuryStatus = json_parsing(data, 'injuryStatus') - self.onTeamId = json_parsing(data, 'onTeamId') - self.lineupSlot = POSITION_MAP.get(data.get('lineupSlotId'), '') - self.position = '' + """Player are part of team""" + + def __init__(self, data, year, pro_team_schedule=None): + self.name = json_parsing(data, "fullName") + self.playerId = json_parsing(data, "id") + self.posRank = json_parsing(data, "positionalRanking") + self.eligibleSlots = [ + POSITION_MAP[pos] for pos in json_parsing(data, "eligibleSlots") + ] + self.acquisitionType = json_parsing(data, "acquisitionType") + self.proTeam = PRO_TEAM_MAP[json_parsing(data, "proTeamId")] + self.jersey = json_parsing(data, "jersey") + self.injuryStatus = json_parsing(data, "injuryStatus") + self.onTeamId = json_parsing(data, "onTeamId") + self.lineupSlot = POSITION_MAP.get(data.get("lineupSlotId"), "") + self.position = "" self.stats = {} self.schedule = {} # Get players main position - for pos in json_parsing(data, 'eligibleSlots'): - if (pos != 25 and '/' not in POSITION_MAP[pos]) or '/' in self.name: + for pos in json_parsing(data, "eligibleSlots"): + if (pos != 25 and "/" not in POSITION_MAP[pos]) or "/" in self.name: self.position = POSITION_MAP[pos] break if pro_team_schedule: - pro_team_id = json_parsing(data, 'proTeamId') + pro_team_id = json_parsing(data, "proTeamId") pro_team = pro_team_schedule.get(pro_team_id, {}) for key in pro_team: game = pro_team[key][0] - team = game['awayProTeamId'] if game['awayProTeamId'] != pro_team_id else game['homeProTeamId'] - self.schedule[key] = { 'team': PRO_TEAM_MAP[team], 'date': datetime.fromtimestamp(game['date']/1000.0) } + team = ( + game["awayProTeamId"] + if game["awayProTeamId"] != pro_team_id + else game["homeProTeamId"] + ) + self.schedule[key] = { + "team": PRO_TEAM_MAP[team], + "date": datetime.fromtimestamp(game["date"] / 1000.0), + } # set each scoring period stat - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] - self.injuryStatus = player.get('injuryStatus', self.injuryStatus) - self.injured = player.get('injured', False) - self.percent_owned = round(player.get('ownership', {}).get('percentOwned', -1), 2) - self.percent_started = round(player.get('ownership', {}).get('percentStarted', -1), 2) + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) + self.injuryStatus = player.get("injuryStatus", self.injuryStatus) + self.injured = player.get("injured", False) + self.percent_owned = round( + player.get("ownership", {}).get("percentOwned", -1), 2 + ) + self.percent_started = round( + player.get("ownership", {}).get("percentStarted", -1), 2 + ) - self.active_status = 'bye' - player_stats = player.get('stats', []) + self.active_status = "bye" + player_stats = player.get("stats", []) for stats in player_stats: - if stats.get('seasonId') != year or stats.get('statSplitTypeId') == 2: + if stats.get("seasonId") != year or stats.get("statSplitTypeId") == 2: continue # real game stats (number of yards, number of passes, etc)- PLAYER_MAP may not be quite correct - stats_breakdown = stats.get('stats', {}) - breakdown = {PLAYER_STATS_MAP.get(int(k), k):v for (k,v) in stats_breakdown.items()} + stats_breakdown = stats.get("stats", {}) + breakdown = { + PLAYER_STATS_MAP.get(int(k), k): v for (k, v) in stats_breakdown.items() + } # fantasy stats (points per td, ppr, points per yard bucket) - applied_stats = stats.get('appliedStats', {}) - points_breakdown = {PLAYER_STATS_MAP.get(int(k), k):v for (k,v) in applied_stats.items()} + applied_stats = stats.get("appliedStats", {}) + points_breakdown = { + PLAYER_STATS_MAP.get(int(k), k): v for (k, v) in applied_stats.items() + } - points = round(stats.get('appliedTotal', 0), 2) - avg_points = round(stats.get('appliedAverage', 0), 2) - scoring_period = stats.get('scoringPeriodId') - stat_source = stats.get('statSourceId') - (points_type, breakdown_type, points_breakdown_type, avg_type) = ('points', 'breakdown', 'points_breakdown', 'avg_points') if stat_source == 0 else ('projected_points', 'projected_breakdown', 'projected_points_breakdown', 'projected_avg_points') + points = round(stats.get("appliedTotal", 0), 2) + avg_points = round(stats.get("appliedAverage", 0), 2) + scoring_period = stats.get("scoringPeriodId") + stat_source = stats.get("statSourceId") + points_type, breakdown_type, points_breakdown_type, avg_type = ( + ("points", "breakdown", "points_breakdown", "avg_points") + if stat_source == 0 + else ( + "projected_points", + "projected_breakdown", + "projected_points_breakdown", + "projected_avg_points", + ) + ) if self.stats.get(scoring_period): self.stats[scoring_period][points_type] = points self.stats[scoring_period][breakdown_type] = breakdown self.stats[scoring_period][points_breakdown_type] = points_breakdown self.stats[scoring_period][avg_type] = avg_points else: - self.stats[scoring_period] = {points_type: points, breakdown_type: breakdown, points_breakdown_type: points_breakdown, avg_type: avg_points} + self.stats[scoring_period] = { + points_type: points, + breakdown_type: breakdown, + points_breakdown_type: points_breakdown, + avg_type: avg_points, + } if not stat_source: if not self.stats[scoring_period][breakdown_type]: - self.active_status = 'inactive' + self.active_status = "inactive" else: - self.active_status = 'active' - self.total_points = self.stats.get(0, {}).get('points', 0) - self.projected_total_points = self.stats.get(0, {}).get('projected_points', 0) - self.avg_points = self.stats.get(0, {}).get('avg_points', 0) - self.projected_avg_points = self.stats.get(0, {}).get('projected_avg_points', 0) + self.active_status = "active" + self.total_points = self.stats.get(0, {}).get("points", 0) + self.projected_total_points = self.stats.get(0, {}).get("projected_points", 0) + self.avg_points = self.stats.get(0, {}).get("avg_points", 0) + self.projected_avg_points = self.stats.get(0, {}).get("projected_avg_points", 0) def __repr__(self): - return f'Player({self.name})' + return f"Player({self.name})" diff --git a/espn_api/football/settings.py b/espn_api/football/settings.py index e782de616..3e98d6f29 100644 --- a/espn_api/football/settings.py +++ b/espn_api/football/settings.py @@ -1,20 +1,25 @@ from ..base_settings import BaseSettings from .constant import SETTINGS_SCORING_FORMAT_MAP, POSITION_MAP + class Settings(BaseSettings): def __init__(self, data): super().__init__(data) self.scoring_format = [] - scoring_items = data['scoringSettings'].get('scoringItems', []) - lineup_slot_counts = data['rosterSettings'].get('lineupSlotCounts', {}) - position_labels = list(POSITION_MAP.values())[:len(lineup_slot_counts)] - self.position_slot_counts = dict(zip(position_labels,list(lineup_slot_counts.values()))) + scoring_items = data["scoringSettings"].get("scoringItems", []) + lineup_slot_counts = data["rosterSettings"].get("lineupSlotCounts", {}) + position_labels = list(POSITION_MAP.values())[: len(lineup_slot_counts)] + self.position_slot_counts = dict( + zip(position_labels, list(lineup_slot_counts.values())) + ) for scoring_item in scoring_items: - stat_id = scoring_item['statId'] - points_override = scoring_item.get('pointsOverrides', {}).get('16') + stat_id = scoring_item["statId"] + points_override = scoring_item.get("pointsOverrides", {}).get("16") - scoring_type = SETTINGS_SCORING_FORMAT_MAP.get(stat_id, { 'abbr': 'Unknown', 'label': 'Unknown' }) - scoring_type['id'] = stat_id - scoring_type['points'] = points_override or scoring_item.get('points', 0) - self.scoring_format.append(scoring_type) \ No newline at end of file + scoring_type = SETTINGS_SCORING_FORMAT_MAP.get( + stat_id, {"abbr": "Unknown", "label": "Unknown"} + ) + scoring_type["id"] = stat_id + scoring_type["points"] = points_override or scoring_item.get("points", 0) + self.scoring_format.append(scoring_type) diff --git a/espn_api/football/team.py b/espn_api/football/team.py index b0c8c863e..54b342d8e 100644 --- a/espn_api/football/team.py +++ b/espn_api/football/team.py @@ -1,90 +1,107 @@ from .player import Player from .constant import PLAYER_STATS_MAP + class Team(object): - '''Teams are part of the league''' + """Teams are part of the league""" + def __init__(self, data, roster, schedule, year, **kwargs): - self.team_id = data['id'] - self.team_abbrev = data['abbrev'] - self.team_name = data.get('name', 'Unknown') - if self.team_name == 'Unknown': - self.team_name = "%s %s" % (data.get('location', 'Unknown'), data.get('nickname', 'Unknown')) - self.division_id = data['divisionId'] - self.division_name = '' # set by caller - self.wins = data['record']['overall']['wins'] - self.losses = data['record']['overall']['losses'] - self.ties = data['record']['overall']['ties'] - self.points_for = data['record']['overall']['pointsFor'] - self.points_against = round(data['record']['overall']['pointsAgainst'], 2) - self.acquisitions = data.get('transactionCounter', {}).get('acquisitions', 0) - self.acquisition_budget_spent = data.get('transactionCounter', {}).get('acquisitionBudgetSpent', 0) - self.drops = data.get('transactionCounter', {}).get('drops', 0) - self.trades = data.get('transactionCounter', {}).get('trades', 0) - self.move_to_ir = data.get('transactionCounter', {}).get('moveToIR', 0) - self.playoff_pct = data.get('currentSimulationResults', {}).get('playoffPct', 0) * 100 - self.draft_projected_rank = data.get('draftDayProjectedRank', 0) - self.streak_length = data['record']['overall']['streakLength'] - self.streak_type = data['record']['overall']['streakType'] - self.standing = data['playoffSeed'] - self.final_standing = data.get('rankFinal') or data.get('rankCalculatedFinal') - self.waiver_rank = data.get('waiverRank', 0) - if 'logo' in data: - self.logo_url = data['logo'] + self.team_id = data["id"] + self.team_abbrev = data["abbrev"] + self.team_name = data.get("name", "Unknown") + if self.team_name == "Unknown": + self.team_name = "%s %s" % ( + data.get("location", "Unknown"), + data.get("nickname", "Unknown"), + ) + self.division_id = data["divisionId"] + self.division_name = "" # set by caller + self.wins = data["record"]["overall"]["wins"] + self.losses = data["record"]["overall"]["losses"] + self.ties = data["record"]["overall"]["ties"] + self.points_for = data["record"]["overall"]["pointsFor"] + self.points_against = round(data["record"]["overall"]["pointsAgainst"], 2) + self.acquisitions = data.get("transactionCounter", {}).get("acquisitions", 0) + self.acquisition_budget_spent = data.get("transactionCounter", {}).get( + "acquisitionBudgetSpent", 0 + ) + self.drops = data.get("transactionCounter", {}).get("drops", 0) + self.trades = data.get("transactionCounter", {}).get("trades", 0) + self.move_to_ir = data.get("transactionCounter", {}).get("moveToIR", 0) + self.playoff_pct = ( + data.get("currentSimulationResults", {}).get("playoffPct", 0) * 100 + ) + self.draft_projected_rank = data.get("draftDayProjectedRank", 0) + self.streak_length = data["record"]["overall"]["streakLength"] + self.streak_type = data["record"]["overall"]["streakType"] + self.standing = data["playoffSeed"] + self.final_standing = data.get("rankFinal") or data.get("rankCalculatedFinal") + self.waiver_rank = data.get("waiverRank", 0) + if "logo" in data: + self.logo_url = data["logo"] else: - self.logo_url = '' + self.logo_url = "" self.roster = [] self.schedule = [] self.scores = [] self.outcomes = [] self.mov = [] self._fetch_schedule(schedule) - self._fetch_roster(roster, year, kwargs.get('pro_schedule')) - self.owners = kwargs.get('owners', []) - self.stats = {PLAYER_STATS_MAP.get(int(i), i): j for i, j in data.get('valuesByStat', {}).items()} + self._fetch_roster(roster, year, kwargs.get("pro_schedule")) + self.owners = kwargs.get("owners", []) + self.stats = { + PLAYER_STATS_MAP.get(int(i), i): j + for i, j in data.get("valuesByStat", {}).items() + } def __repr__(self): - return 'Team(%s)' % (self.team_name, ) - - def _fetch_roster(self, data, year, pro_schedule = None): - '''Fetch teams roster''' + return "Team(%s)" % (self.team_name,) + + def _fetch_roster(self, data, year, pro_schedule=None): + """Fetch teams roster""" self.roster.clear() - roster = data.get('entries', []) + roster = data.get("entries", []) for player in roster: self.roster.append(Player(player, year, pro_schedule)) def _fetch_schedule(self, data): - '''Fetch schedule and scores for team''' + """Fetch schedule and scores for team""" for matchup in data: - home_team = matchup.get('home', {}) - away_team = matchup.get('away', {}) - home_id = home_team.get('teamId', -1) - away_id = away_team.get('teamId', -1) + home_team = matchup.get("home", {}) + away_team = matchup.get("away", {}) + home_id = home_team.get("teamId", -1) + away_id = away_team.get("teamId", -1) if self.team_id in (home_id, away_id): # find if current team is home or away - (current_team, opponent_id, away) = (home_team, away_id, False) if home_id == self.team_id else (away_team, home_id, True) + current_team, opponent_id, away = ( + (home_team, away_id, False) + if home_id == self.team_id + else (away_team, home_id, True) + ) # if bye week set opponent id to self - if opponent_id == -1: opponent_id = self.team_id + if opponent_id == -1: + opponent_id = self.team_id - score = current_team.get('totalPoints') - self.outcomes.append(self._get_winner(matchup['winner'], away)) + score = current_team.get("totalPoints") + self.outcomes.append(self._get_winner(matchup["winner"], away)) self.scores.append(score) self.schedule.append(opponent_id) - + def _get_winner(self, winner: str, is_away: bool) -> str: - if winner == 'UNDECIDED': - return 'U' - elif winner == 'TIE': - return 'T' - elif (is_away and winner == 'AWAY') or (not is_away and winner == 'HOME'): - return 'W' + if winner == "UNDECIDED": + return "U" + elif winner == "TIE": + return "T" + elif (is_away and winner == "AWAY") or (not is_away and winner == "HOME"): + return "W" else: - return 'L' + return "L" def get_player_name(self, playerId: int) -> str: for player in self.roster: if player.playerId == playerId: return player.name - return '' + return "" diff --git a/espn_api/football/transaction.py b/espn_api/football/transaction.py index 070be4d72..7afa5b299 100644 --- a/espn_api/football/transaction.py +++ b/espn_api/football/transaction.py @@ -1,26 +1,27 @@ class Transaction(object): def __init__(self, data, player_map, get_team_data): - self.team = get_team_data(data['teamId']) - self.type = data['type'] - self.status = data['status'] - self.scoring_period = data['scoringPeriodId'] - self.date = data.get('processDate') + self.team = get_team_data(data["teamId"]) + self.type = data["type"] + self.status = data["status"] + self.scoring_period = data["scoringPeriodId"] + self.date = data.get("processDate") if not self.date: - self.date = data.get('proposedDate') - self.bid_amount = data.get('bidAmount') + self.date = data.get("proposedDate") + self.bid_amount = data.get("bidAmount") self.items = [] - for item in data['items']: + for item in data["items"]: self.items.append(TransactionItem(item, player_map)) def __repr__(self): - items = ', '.join([str(item) for item in self.items]) - return f'Transaction({self.team.team_name} {self.type} {items})' + items = ", ".join([str(item) for item in self.items]) + return f"Transaction({self.team.team_name} {self.type} {items})" + class TransactionItem(object): def __init__(self, data, player_map): - self.type = data['type'] - self.playerId = data['playerId'] - self.player = player_map.get(data['playerId'], 'Unknown') + self.type = data["type"] + self.playerId = data["playerId"] + self.player = player_map.get(data["playerId"], "Unknown") def __repr__(self): - return f'{self.type} {self.player}' \ No newline at end of file + return f"{self.type} {self.player}" diff --git a/espn_api/football/utils.py b/espn_api/football/utils.py index e631657ed..01dbeaf5b 100644 --- a/espn_api/football/utils.py +++ b/espn_api/football/utils.py @@ -1,5 +1,6 @@ # Helper functions for json parsing and power rankings + def json_parsing(obj, key): """Recursively pull values of specified key from nested JSON.""" arr = [] @@ -8,7 +9,9 @@ def extract(obj, arr, key): """Return all matching values in an object.""" if isinstance(obj, dict): for k, v in obj.items(): - if isinstance(v, (dict)) or (isinstance(v, (list)) and v and isinstance(v[0], (list, dict))): + if isinstance(v, (dict)) or ( + isinstance(v, (list)) and v and isinstance(v[0], (list, dict)) + ): extract(v, arr, key) elif k == key: arr.append(v) @@ -20,8 +23,9 @@ def extract(obj, arr, key): results = extract(obj, arr, key) return results[0] if results else results + def square_matrix(X): - '''Squares a matrix''' + """Squares a matrix""" result = [[0.0 for x in range(len(X))] for y in range(len(X))] # iterate through rows of X @@ -38,7 +42,7 @@ def square_matrix(X): def add_matrix(X, Y): - '''Adds two matrices''' + """Adds two matrices""" result = [[0.0 for x in range(len(X))] for y in range(len(X))] for i in range(len(X)): @@ -51,14 +55,14 @@ def add_matrix(X, Y): def two_step_dominance(X): - '''Returns result of two step dominance formula''' + """Returns result of two step dominance formula""" matrix = add_matrix(square_matrix(X), X) result = [sum(x) for x in matrix] return result def power_points(dominance, teams, week): - '''Returns list of power points''' + """Returns list of power points""" if week <= 0: week = 1 power_points = [] @@ -66,8 +70,9 @@ def power_points(dominance, teams, week): avg_score = sum(team.scores[:week]) / week avg_mov = sum(team.mov[:week]) / week - power = '{0:.2f}'.format((int(i)*0.8) + (int(avg_score)*0.15) + - (int(avg_mov)*0.05)) + power = "{0:.2f}".format( + (int(i) * 0.8) + (int(avg_score) * 0.15) + (int(avg_mov) * 0.05) + ) power_points.append(power) power_tup = [(i, j) for (i, j) in zip(power_points, teams)] - return sorted(power_tup, key=lambda tup: float(tup[0]), reverse=True) \ No newline at end of file + return sorted(power_tup, key=lambda tup: float(tup[0]), reverse=True) diff --git a/espn_api/hockey/__init__.py b/espn_api/hockey/__init__.py index 74026ae2c..30df1a34d 100644 --- a/espn_api/hockey/__init__.py +++ b/espn_api/hockey/__init__.py @@ -1,11 +1,4 @@ -__all__ = ['League', - 'Team', - 'Player', - 'Record', - 'Team', - 'POSITION_MAP', - 'PRO_TEAM_MAP' - ] +__all__ = ["League", "Team", "Player", "Record", "Team", "POSITION_MAP", "PRO_TEAM_MAP"] from .league import League from .player import Player diff --git a/espn_api/hockey/activity.py b/espn_api/hockey/activity.py index 8285e5e0b..92b6fad58 100644 --- a/espn_api/hockey/activity.py +++ b/espn_api/hockey/activity.py @@ -1,25 +1,26 @@ from espn_api.hockey.constant import ACTIVITY_MAP + class Activity(object): def __init__(self, data, player_map, get_team_data): self.actions = [] # List of tuples (Team, action, player) - self.date = data['date'] - for msg in data['messages']: - team = '' - action = 'UNKNOWN' - player = '' - msg_id = msg['messageTypeId'] + self.date = data["date"] + for msg in data["messages"]: + team = "" + action = "UNKNOWN" + player = "" + msg_id = msg["messageTypeId"] if msg_id == 244: - team = get_team_data(msg['from']) + team = get_team_data(msg["from"]) elif msg_id == 239: - team = get_team_data(msg['for']) + team = get_team_data(msg["for"]) else: - team = get_team_data(msg['to']) + team = get_team_data(msg["to"]) if msg_id in ACTIVITY_MAP: action = ACTIVITY_MAP[msg_id] - if msg['targetId'] in player_map: - player = player_map[msg['targetId']] + if msg["targetId"] in player_map: + player = player_map[msg["targetId"]] self.actions.append((team, action, player)) def __repr__(self): - return 'Activity(' + ' '.join("(%s,%s,%s)" % tup for tup in self.actions) + ')' + return "Activity(" + " ".join("(%s,%s,%s)" % tup for tup in self.actions) + ")" diff --git a/espn_api/hockey/box_player.py b/espn_api/hockey/box_player.py index 6d955982e..b2c0780d4 100644 --- a/espn_api/hockey/box_player.py +++ b/espn_api/hockey/box_player.py @@ -4,32 +4,41 @@ class BoxPlayer(Player): - '''player with extra data from a matchup''' + """player with extra data from a matchup""" def __init__(self, data, pro_schedule): super(BoxPlayer, self).__init__(data) - self.slot_position = 'FA' + self.slot_position = "FA" self.pro_opponent = "None" # professional team playing against self.game_played = 100 # 0-100 for percent of game played self.points = 0 self.points_breakdown = {} - if 'lineupSlotId' in data: - self.slot_position = POSITION_MAP[data['lineupSlotId']] + if "lineupSlotId" in data: + self.slot_position = POSITION_MAP[data["lineupSlotId"]] - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] - if player['proTeamId'] in pro_schedule: - (opp_id, date) = pro_schedule[player['proTeamId']] - self.game_played = 100 if datetime.now() > datetime.fromtimestamp(date / 1000.0) + timedelta(hours=3) else 0 - self.pro_opponent = PRO_TEAM_MAP.get(opp_id, 'Unknown Team') + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) + if player["proTeamId"] in pro_schedule: + opp_id, date = pro_schedule[player["proTeamId"]] + self.game_played = ( + 100 + if datetime.now() + > datetime.fromtimestamp(date / 1000.0) + timedelta(hours=3) + else 0 + ) + self.pro_opponent = PRO_TEAM_MAP.get(opp_id, "Unknown Team") - player_stats = player.get('stats', []) + player_stats = player.get("stats", []) for stats in player_stats: - stats_breakdown = stats.get('appliedStats') or stats.get('stats', {}) + stats_breakdown = stats.get("appliedStats") or stats.get("stats", {}) breakdown = {STATS_MAP.get(k, k): v for (k, v) in stats_breakdown.items()} - points = round(stats.get('appliedTotal', 0), 2) + points = round(stats.get("appliedTotal", 0), 2) self.points = points self.points_breakdown = breakdown def __repr__(self): - return f'Player({self.name}, points:{self.points})' + return f"Player({self.name}, points:{self.points})" diff --git a/espn_api/hockey/box_score.py b/espn_api/hockey/box_score.py index de89a6e77..e9de8c667 100644 --- a/espn_api/hockey/box_score.py +++ b/espn_api/hockey/box_score.py @@ -2,36 +2,48 @@ class BoxScore(object): - ''' ''' + """ """ + def __init__(self, data, pro_schedule, by_matchup): - self.winner = data['winner'] - self.home_team = data['home']['teamId'] + self.winner = data["winner"] + self.home_team = data["home"]["teamId"] self.home_projected = -1 # week is over/not set - roster_key = 'rosterForMatchupPeriod' if by_matchup else 'rosterForCurrentScoringPeriod' - home_roster = data['home'].get(roster_key, {}) - if 'totalPointsLive' in data['home']: - self.home_score = round(data['home']['totalPointsLive'], 2) - self.home_projected = round(data['home'].get('totalProjectedPointsLive', -1), 2) + roster_key = ( + "rosterForMatchupPeriod" if by_matchup else "rosterForCurrentScoringPeriod" + ) + home_roster = data["home"].get(roster_key, {}) + if "totalPointsLive" in data["home"]: + self.home_score = round(data["home"]["totalPointsLive"], 2) + self.home_projected = round( + data["home"].get("totalProjectedPointsLive", -1), 2 + ) else: - self.home_score = round(home_roster.get('appliedStatTotal', 0), 2) - self.home_lineup = [BoxPlayer(player, pro_schedule) for player in home_roster.get('entries', [])] + self.home_score = round(home_roster.get("appliedStatTotal", 0), 2) + self.home_lineup = [ + BoxPlayer(player, pro_schedule) for player in home_roster.get("entries", []) + ] # For Leagues with bye weeks self.away_team = 0 self.away_score = 0 self.away_lineup = [] self.away_projected = -1 # week is over/not set - if 'away' in data: - self.away_team = data['away']['teamId'] - away_roster = data['away'].get(roster_key, {}) - if 'totalPointsLive' in data['away']: - self.away_score = round(data['away']['totalPointsLive'], 2) - self.away_projected = round(data['away'].get('totalProjectedPointsLive', -1), 2) + if "away" in data: + self.away_team = data["away"]["teamId"] + away_roster = data["away"].get(roster_key, {}) + if "totalPointsLive" in data["away"]: + self.away_score = round(data["away"]["totalPointsLive"], 2) + self.away_projected = round( + data["away"].get("totalProjectedPointsLive", -1), 2 + ) else: - self.away_score = round(away_roster.get('appliedStatTotal', 0), 2) - self.away_lineup = [BoxPlayer(player, pro_schedule) for player in away_roster.get('entries', [])] + self.away_score = round(away_roster.get("appliedStatTotal", 0), 2) + self.away_lineup = [ + BoxPlayer(player, pro_schedule) + for player in away_roster.get("entries", []) + ] def __repr__(self): away_team = self.away_team or "BYE" home_team = self.home_team or "BYE" - return f'Box Score({away_team} at {home_team})' \ No newline at end of file + return f"Box Score({away_team} at {home_team})" diff --git a/espn_api/hockey/constant.py b/espn_api/hockey/constant.py index c827db3a1..6c12ff1a8 100644 --- a/espn_api/hockey/constant.py +++ b/espn_api/hockey/constant.py @@ -1,128 +1,128 @@ -#Constants +# Constants POSITION_MAP = { - 0 : 'Center' - , 1 : 'Left Wing' - , 2 : 'Right Wing' - , 3 : 'Forward' - , 4 : 'Defense' - , 5 : 'Goalie' - , 6 : 'Util' - , 7 : 'Bench' - , 8 : 'IR' - , 'Center': 0 - , 'Left Wing' : 1 - , 'Right Wing' : 2 - , 'Forward' : 3 - , 'Defense' : 4 - , 'Goalie' : 5 - , 'Util' : 6 - , 'Bench' : 7 - , 'IR' : 8 + 0: "Center", + 1: "Left Wing", + 2: "Right Wing", + 3: "Forward", + 4: "Defense", + 5: "Goalie", + 6: "Util", + 7: "Bench", + 8: "IR", + "Center": 0, + "Left Wing": 1, + "Right Wing": 2, + "Forward": 3, + "Defense": 4, + "Goalie": 5, + "Util": 6, + "Bench": 7, + "IR": 8, } STATS_IDENTIFIER = { - '00': 'Total', - '01': 'Last 7', - '02': 'Last 15', - '03': 'Last 30', - '10': 'Projected', - '20': '20' + "00": "Total", + "01": "Last 7", + "02": "Last 15", + "03": "Last 30", + "10": "Projected", + "20": "20", } PRO_TEAM_MAP = { - 1: 'Boston Bruins' - , 2: 'Buffalo Sabres' - , 3: 'Calgary Flames' - , 4: 'Chicago Blackhawks' - , 5: 'Detroit Red Wings' - , 6: 'Edmonton Oilers' - , 7: 'Carolina Hurricanes' - , 8: 'Los Angeles Kings' - , 9: 'Dallas Stars' - , 10: 'Montréal Canadiens' - , 11: 'New Jersey Devils' - , 12: 'New York Islanders' - , 13: 'New York Rangers' - , 14: 'Ottawa Senators' - , 15: 'Philadelphia Flyers' - , 16: 'Pittsburgh Penguins' - , 17: 'Colorado Avalanche' - , 18: 'San Jose Sharks' - , 19: 'St. Louis Blues' - , 20: 'Tampa Bay Lightning' - , 21: 'Toronto Maple Leafs' - , 22: 'Vancouver Canucks' - , 23: 'Washington Capitals' - , 24: 'Arizona Coyotes' - , 25: 'Anaheim Ducks' - , 26: 'Florida Panthers' - , 27: 'Nashville Predators' - , 28: 'Winnipeg Jets' - , 29: 'Columbus Blue Jackets' - , 30: 'Minnesota Wild' - , 37: 'Vegas Golden Knights' - , 124292: 'Seattle Kraken' - , 129764: 'Utah Hockey Club' + 1: "Boston Bruins", + 2: "Buffalo Sabres", + 3: "Calgary Flames", + 4: "Chicago Blackhawks", + 5: "Detroit Red Wings", + 6: "Edmonton Oilers", + 7: "Carolina Hurricanes", + 8: "Los Angeles Kings", + 9: "Dallas Stars", + 10: "Montréal Canadiens", + 11: "New Jersey Devils", + 12: "New York Islanders", + 13: "New York Rangers", + 14: "Ottawa Senators", + 15: "Philadelphia Flyers", + 16: "Pittsburgh Penguins", + 17: "Colorado Avalanche", + 18: "San Jose Sharks", + 19: "St. Louis Blues", + 20: "Tampa Bay Lightning", + 21: "Toronto Maple Leafs", + 22: "Vancouver Canucks", + 23: "Washington Capitals", + 24: "Arizona Coyotes", + 25: "Anaheim Ducks", + 26: "Florida Panthers", + 27: "Nashville Predators", + 28: "Winnipeg Jets", + 29: "Columbus Blue Jackets", + 30: "Minnesota Wild", + 37: "Vegas Golden Knights", + 124292: "Seattle Kraken", + 129764: "Utah Hockey Club", } STATS_MAP = { - '0': 'GS', - '1': 'W', - '2': 'L', - '3': 'SA', - '4': 'GA', - '5': '5', - '6': 'SV', - '7': 'SO', - '8': 'MIN ?', - '9': 'OTL', - '10': 'GAA', - '11': 'SV%', - '12': '12', - '13': 'G', - '14': 'A', - '15': '+/-', - '16': '16', - '17': 'PIM', - '18': 'PPG', - '19': 'PPA', - '20': 'SHG', - '21': 'SHA', - '22': 'GWG', - '23': 'FOW', - '24': 'FOL', - '25': '25', - '26': 'TTOI ?', - '27': 'ATOI', - '28': 'HAT', - '29': 'SOG', - '30': '30', - '31': 'HIT', - '32': 'BLK', - '33': 'DEF', - '34': 'GP', - '35': 'STPG', - '36': 'STPA', - '37': 'STP', - '38': 'PPP', - '39': 'SHP', - '40': '40', - '41': '41', - '42': '42', - '43': '43', - '44': '44', - '45': '45', - '99': '99' - } + "0": "GS", + "1": "W", + "2": "L", + "3": "SA", + "4": "GA", + "5": "5", + "6": "SV", + "7": "SO", + "8": "MIN ?", + "9": "OTL", + "10": "GAA", + "11": "SV%", + "12": "12", + "13": "G", + "14": "A", + "15": "+/-", + "16": "16", + "17": "PIM", + "18": "PPG", + "19": "PPA", + "20": "SHG", + "21": "SHA", + "22": "GWG", + "23": "FOW", + "24": "FOL", + "25": "25", + "26": "TTOI ?", + "27": "ATOI", + "28": "HAT", + "29": "SOG", + "30": "30", + "31": "HIT", + "32": "BLK", + "33": "DEF", + "34": "GP", + "35": "STPG", + "36": "STPA", + "37": "STP", + "38": "PPP", + "39": "SHP", + "40": "40", + "41": "41", + "42": "42", + "43": "43", + "44": "44", + "45": "45", + "99": "99", +} ACTIVITY_MAP = { - 178: 'FA ADDED', - 180: 'WAIVER ADDED', - 179: 'DROPPED', - 181: 'DROPPED', - 239: 'DROPPED', - 244: 'TRADED', - 'FA': 178, - 'WAIVER': 180, - 'TRADED': 244 + 178: "FA ADDED", + 180: "WAIVER ADDED", + 179: "DROPPED", + 181: "DROPPED", + 239: "DROPPED", + 244: "TRADED", + "FA": 178, + "WAIVER": 180, + "TRADED": 244, } diff --git a/espn_api/hockey/league.py b/espn_api/hockey/league.py index ed13df65b..4d97e9103 100644 --- a/espn_api/hockey/league.py +++ b/espn_api/hockey/league.py @@ -12,10 +12,25 @@ class League(BaseLeague): - '''Creates a League instance for Public/Private ESPN league''' - - def __init__(self, league_id: int, year: int, espn_s2=None, swid=None, fetch_league=True, debug=False): - super().__init__(league_id=league_id, year=year, sport='nhl', espn_s2=espn_s2, swid=swid, debug=debug) + """Creates a League instance for Public/Private ESPN league""" + + def __init__( + self, + league_id: int, + year: int, + espn_s2=None, + swid=None, + fetch_league=True, + debug=False, + ): + super().__init__( + league_id=league_id, + year=year, + sport="nhl", + espn_s2=espn_s2, + swid=swid, + debug=debug, + ) if fetch_league: self.fetch_league() @@ -28,28 +43,31 @@ def fetch_league(self): def _fetch_league(self): data = super()._fetch_league() self._fetch_players() - self._map_matchup_ids(data['schedule']) + self._map_matchup_ids(data["schedule"]) return data def _map_matchup_ids(self, schedule): self.matchup_ids = {} for match in schedule: - matchup_period = match.get('matchupPeriodId') - scoring_periods = match.get('home', {}).get('pointsByScoringPeriod', {}).keys() + matchup_period = match.get("matchupPeriodId") + scoring_periods = ( + match.get("home", {}).get("pointsByScoringPeriod", {}).keys() + ) if len(scoring_periods) > 0: if matchup_period not in self.matchup_ids: self.matchup_ids[matchup_period] = sorted(scoring_periods) else: self.matchup_ids[matchup_period] = sorted( - set(self.matchup_ids[matchup_period] + list(scoring_periods))) + set(self.matchup_ids[matchup_period] + list(scoring_periods)) + ) def _fetch_teams(self, data): - '''Fetch teams in league''' + """Fetch teams in league""" super()._fetch_teams(data, TeamClass=Team) # replace opponentIds in schedule with team instances for team in self.teams: - team.division_name = self.settings.division_map.get(team.division_id, '') + team.division_name = self.settings.division_map.get(team.division_id, "") for week, matchup in enumerate(team.schedule): for opponent in self.teams: if matchup.away_team == opponent.team_id: @@ -57,24 +75,30 @@ def _fetch_teams(self, data): if matchup.home_team == opponent.team_id: matchup.home_team = opponent - def standings(self) -> List[Team]: - '''Fetch teams in league sorted by standing''' - standings = sorted(self.teams, key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, - reverse=False) + """Fetch teams in league sorted by standing""" + standings = sorted( + self.teams, + key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, + reverse=False, + ) return standings def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: - '''Returns list of matchups for a given matchup period''' + """Returns list of matchups for a given matchup period""" if not matchupPeriod: - matchupPeriod=self.currentMatchupPeriod + matchupPeriod = self.currentMatchupPeriod params = { - 'view': 'mMatchup', + "view": "mMatchup", } data = self.espn_request.league_get(params=params) - schedule = data['schedule'] - matchups = [Matchup(matchup) for matchup in schedule if matchup['matchupPeriodId'] == matchupPeriod] + schedule = data["schedule"] + matchups = [ + Matchup(matchup) + for matchup in schedule + if matchup["matchupPeriodId"] == matchupPeriod + ] for team in self.teams: for matchup in matchups: @@ -85,37 +109,51 @@ def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: return matchups - - def recent_activity(self, size: int = 25, msg_type: str = None, offset: int = 0) -> List[Activity]: - '''Returns a list of recent league activities (Add, Drop, Trade)''' + def recent_activity( + self, size: int = 25, msg_type: str = None, offset: int = 0 + ) -> List[Activity]: + """Returns a list of recent league activities (Add, Drop, Trade)""" if self.year < 2019: - raise Exception('Cant use recent activity before 2019') + raise Exception("Cant use recent activity before 2019") msg_types = [178, 180, 179, 239, 181, 244] if msg_type in ACTIVITY_MAP: msg_types = [ACTIVITY_MAP[msg_type]] - params = { - 'view': 'kona_league_communication' - } + params = {"view": "kona_league_communication"} - filters = {"topics": {"filterType": {"value": ["ACTIVITY_TRANSACTIONS"]}, "limit": size, - "limitPerMessageSet": {"value": 25}, "offset": offset, - "sortMessageDate": {"sortPriority": 1, "sortAsc": False}, - "sortFor": {"sortPriority": 2, "sortAsc": False}, - "filterIncludeMessageTypeIds": {"value": msg_types}}} - headers = {'x-fantasy-filter': json.dumps(filters)} - data = self.espn_request.league_get(extend='/communication/', params=params, headers=headers) - data = data['topics'] - activity = [Activity(topic, self.player_map, self.get_team_data) for topic in data] + filters = { + "topics": { + "filterType": {"value": ["ACTIVITY_TRANSACTIONS"]}, + "limit": size, + "limitPerMessageSet": {"value": 25}, + "offset": offset, + "sortMessageDate": {"sortPriority": 1, "sortAsc": False}, + "sortFor": {"sortPriority": 2, "sortAsc": False}, + "filterIncludeMessageTypeIds": {"value": msg_types}, + } + } + headers = {"x-fantasy-filter": json.dumps(filters)} + data = self.espn_request.league_get( + extend="/communication/", params=params, headers=headers + ) + data = data["topics"] + activity = [ + Activity(topic, self.player_map, self.get_team_data) for topic in data + ] return activity - def free_agents(self, week: int = None, size: int = 50, position: str = None, position_id: int = None) -> List[ - Player]: - '''Returns a List of Free Agents for a Given Week - Should only be used with most recent season''' + def free_agents( + self, + week: int = None, + size: int = 50, + position: str = None, + position_id: int = None, + ) -> List[Player]: + """Returns a List of Free Agents for a Given Week + Should only be used with most recent season""" if self.year < 2019: - raise Exception('Cant use free agents before 2019') + raise Exception("Cant use free agents before 2019") if not week: week = self.current_week @@ -126,26 +164,39 @@ def free_agents(self, week: int = None, size: int = 50, position: str = None, po slot_filter.append(position_id) params = { - 'view': 'kona_player_info', - 'scoringPeriodId': week, + "view": "kona_player_info", + "scoringPeriodId": week, } filters = { - "players": {"filterStatus": {"value": ["FREEAGENT", "WAIVERS"]}, "filterSlotIds": {"value": slot_filter}, - "limit": size, "sortPercOwned": {"sortPriority": 1, "sortAsc": False}, - "sortDraftRanks": {"sortPriority": 100, "sortAsc": True, "value": "STANDARD"}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + "players": { + "filterStatus": {"value": ["FREEAGENT", "WAIVERS"]}, + "filterSlotIds": {"value": slot_filter}, + "limit": size, + "sortPercOwned": {"sortPriority": 1, "sortAsc": False}, + "sortDraftRanks": { + "sortPriority": 100, + "sortAsc": True, + "value": "STANDARD", + }, + } + } + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - players = data['players'] + players = data["players"] free_agents = [Player(player) for player in players] return free_agents - def box_scores(self, matchup_period: int = None, scoring_period: int = None, matchup_total: bool = True) -> List[ - BoxScore]: - '''Returns list of box score for a given matchup or scoring period''' + def box_scores( + self, + matchup_period: int = None, + scoring_period: int = None, + matchup_total: bool = True, + ) -> List[BoxScore]: + """Returns list of box score for a given matchup or scoring period""" if self.year < 2019: - raise Exception('Cant use box score before 2019') + raise Exception("Cant use box score before 2019") matchup_id = self.currentMatchupPeriod scoring_id = self.current_week @@ -154,7 +205,11 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat scoring_id = scoring_period elif matchup_period and matchup_period < matchup_id: matchup_id = matchup_period - scoring_id = self.matchup_ids[matchup_period][-1] if matchup_period in self.matchup_ids else 1 + scoring_id = ( + self.matchup_ids[matchup_period][-1] + if matchup_period in self.matchup_ids + else 1 + ) elif scoring_period and scoring_period <= scoring_id: scoring_id = scoring_period for matchup in self.matchup_ids.keys(): @@ -163,17 +218,19 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat break params = { - 'view': ['mMatchupScore', 'mScoreboard'], - 'scoringPeriodId': scoring_id + "view": ["mMatchupScore", "mScoreboard"], + "scoringPeriodId": scoring_id, } filters = {"schedule": {"filterMatchupPeriodIds": {"value": [matchup_id]}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - schedule = data['schedule'] + schedule = data["schedule"] pro_schedule = self._get_pro_schedule(scoring_id) - box_data = [BoxScore(matchup, pro_schedule, matchup_total) for matchup in schedule] + box_data = [ + BoxScore(matchup, pro_schedule, matchup_total) for matchup in schedule + ] for team in self.teams: for matchup in box_data: @@ -182,4 +239,3 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat elif matchup.away_team == team.team_id: matchup.away_team = team return box_data - diff --git a/espn_api/hockey/matchup.py b/espn_api/hockey/matchup.py index 4282dcddf..e24c4e562 100644 --- a/espn_api/hockey/matchup.py +++ b/espn_api/hockey/matchup.py @@ -1,7 +1,9 @@ from .constant import STATS_MAP + class Matchup(object): - '''Creates Matchup instance''' + """Creates Matchup instance""" + def __init__(self, data): self.home_team_live_score = None self.away_team_live_score = None @@ -12,31 +14,51 @@ def __repr__(self): # writing this too early to see if data['home']['totalPoints'] is final score # it might also be used for points leagues instead of category leagues if not self.away_team_live_score: - return f'Matchup({self.home_team}, {self.away_team})' + return f"Matchup({self.home_team}, {self.away_team})" else: - return f'Matchup({self.home_team} {round(self.home_team_live_score, 1)} - {round(self.away_team_live_score, 1)} {self.away_team})' + return f"Matchup({self.home_team} {round(self.home_team_live_score, 1)} - {round(self.away_team_live_score, 1)} {self.away_team})" def _fetch_matchup_info(self, data): - '''Fetch info for matchup''' - self.home_team = data['home']['teamId'] - self.home_final_score = data['home']['totalPoints'] - self.away_team = data['away']['teamId'] - self.away_final_score = data['away']['totalPoints'] - self.winner = data['winner'] + """Fetch info for matchup""" + self.home_team = data["home"]["teamId"] + self.home_final_score = data["home"]["totalPoints"] + self.away_team = data["away"]["teamId"] + self.away_final_score = data["away"]["totalPoints"] + self.winner = data["winner"] self.home_team_cats = None self.away_team_cats = None # if stats are available - if 'cumulativeScore' in data['home'].keys() and data['home']['cumulativeScore']['scoreByStat']: - - self.home_team_live_score = (data['home']['cumulativeScore']['wins'] + - data['home']['cumulativeScore']['ties']/2) - self.away_team_live_score = (data['away']['cumulativeScore']['wins'] + - data['away']['cumulativeScore']['ties']/2) + if ( + "cumulativeScore" in data["home"].keys() + and data["home"]["cumulativeScore"]["scoreByStat"] + ): - self.home_team_cats = { STATS_MAP[i]: {'score': data['home']['cumulativeScore']['scoreByStat'][i]['score'], - 'result': data['home']['cumulativeScore']['scoreByStat'][i]['result']} for i in data['home']['cumulativeScore']['scoreByStat'].keys()} + self.home_team_live_score = ( + data["home"]["cumulativeScore"]["wins"] + + data["home"]["cumulativeScore"]["ties"] / 2 + ) + self.away_team_live_score = ( + data["away"]["cumulativeScore"]["wins"] + + data["away"]["cumulativeScore"]["ties"] / 2 + ) - self.away_team_cats = { STATS_MAP[i]: {'score': data['away']['cumulativeScore']['scoreByStat'][i]['score'], - 'result': data['away']['cumulativeScore']['scoreByStat'][i]['result']} for i in data['away']['cumulativeScore']['scoreByStat'].keys()} + self.home_team_cats = { + STATS_MAP[i]: { + "score": data["home"]["cumulativeScore"]["scoreByStat"][i]["score"], + "result": data["home"]["cumulativeScore"]["scoreByStat"][i][ + "result" + ], + } + for i in data["home"]["cumulativeScore"]["scoreByStat"].keys() + } + self.away_team_cats = { + STATS_MAP[i]: { + "score": data["away"]["cumulativeScore"]["scoreByStat"][i]["score"], + "result": data["away"]["cumulativeScore"]["scoreByStat"][i][ + "result" + ], + } + for i in data["away"]["cumulativeScore"]["scoreByStat"].keys() + } diff --git a/espn_api/hockey/player.py b/espn_api/hockey/player.py index 99a53efe6..79a8a3b06 100644 --- a/espn_api/hockey/player.py +++ b/espn_api/hockey/player.py @@ -5,19 +5,27 @@ class Player(object): def __init__(self, data): - self.name = json_parsing(data, 'fullName') - self.playerId = json_parsing(data, 'id') - self.position = POSITION_MAP.get(json_parsing(data, 'defaultPositionId') - 1 - if json_parsing(data, 'defaultPositionId') and json_parsing(data, 'defaultPositionId') <= 3 - else json_parsing(data, 'defaultPositionId'), '') - self.lineupSlot = POSITION_MAP.get(data.get('lineupSlotId'), '') - self.eligibleSlots = [POSITION_MAP.get(pos, '') for pos in json_parsing(data, 'eligibleSlots')] - self.acquisitionType = json_parsing(data, 'acquisitionType') - self.proTeam = PRO_TEAM_MAP.get(json_parsing(data, 'proTeamId'), 'Unknown Team') - self.injuryStatus = json_parsing(data, 'injuryStatus') + self.name = json_parsing(data, "fullName") + self.playerId = json_parsing(data, "id") + self.position = POSITION_MAP.get( + ( + json_parsing(data, "defaultPositionId") - 1 + if json_parsing(data, "defaultPositionId") + and json_parsing(data, "defaultPositionId") <= 3 + else json_parsing(data, "defaultPositionId") + ), + "", + ) + self.lineupSlot = POSITION_MAP.get(data.get("lineupSlotId"), "") + self.eligibleSlots = [ + POSITION_MAP.get(pos, "") for pos in json_parsing(data, "eligibleSlots") + ] + self.acquisitionType = json_parsing(data, "acquisitionType") + self.proTeam = PRO_TEAM_MAP.get(json_parsing(data, "proTeamId"), "Unknown Team") + self.injuryStatus = json_parsing(data, "injuryStatus") self.stats = {} - ''' + """ Options 1. Today 2. This season (2021) 002021 @@ -26,30 +34,34 @@ def __init__(self, data): 5. Last 30 032021 6. Last season (2020) 002020 7. 2021 Projections 102021 - ''' - player = data.get('playerPoolEntry', {}).get('player') or data['player'] - self.injuryStatus = player.get('injuryStatus', self.injuryStatus) - self.injured = player.get('injured', False) - - for split in player.get('stats', []): - if split.get('stats'): - id = split['id'] + """ + player = data.get("playerPoolEntry", {}).get("player") or data["player"] + self.injuryStatus = player.get("injuryStatus", self.injuryStatus) + self.injured = player.get("injured", False) + + for split in player.get("stats", []): + if split.get("stats"): + id = split["id"] stat_key = get_stat_key(id) self.stats[stat_key] = {} - if 'stats' in split.keys(): - self.stats[stat_key]['total'] = {STATS_MAP[i]: split['stats'][i] for i in split['stats'].keys() - if STATS_MAP[i] != ''} + if "stats" in split.keys(): + self.stats[stat_key]["total"] = { + STATS_MAP[i]: split["stats"][i] + for i in split["stats"].keys() + if STATS_MAP[i] != "" + } else: - self.stats[stat_key]['total'] = None + self.stats[stat_key]["total"] = None def __repr__(self): - return 'Player(%s)' % (self.name,) + return "Player(%s)" % (self.name,) + def get_stat_key(id: str) -> str: if id[:2] in STATS_IDENTIFIER: stat_type = STATS_IDENTIFIER[id[:2]] - return stat_type + ' ' + id[2:] + return stat_type + " " + id[2:] return id diff --git a/espn_api/hockey/record.py b/espn_api/hockey/record.py index 86ade0bf5..6cb43a27f 100644 --- a/espn_api/hockey/record.py +++ b/espn_api/hockey/record.py @@ -1,23 +1,23 @@ class Record(object): - + def __init__(self, data): - self.games_back = data['gamesBack'] - self.losses = data['losses'] - self.points_against = data['pointsAgainst'] - self.points_for = data['pointsFor'] - self.ties = data['ties'] - self.wins = data['wins'] - + self.games_back = data["gamesBack"] + self.losses = data["losses"] + self.points_against = data["pointsAgainst"] + self.points_for = data["pointsFor"] + self.ties = data["ties"] + self.wins = data["wins"] + def __add__(self, otherRecord): data = {} - data['gamesBack'] = self.games_back + otherRecord.games_back - data['losses'] = self.losses + otherRecord.losses - data['pointsAgainst'] = self.points_against + otherRecord.points_against - data['pointsFor'] = self.points_for + otherRecord.points_for - data['ties'] = self.ties + otherRecord.ties - data['wins'] = self.wins + otherRecord.wins + data["gamesBack"] = self.games_back + otherRecord.games_back + data["losses"] = self.losses + otherRecord.losses + data["pointsAgainst"] = self.points_against + otherRecord.points_against + data["pointsFor"] = self.points_for + otherRecord.points_for + data["ties"] = self.ties + otherRecord.ties + data["wins"] = self.wins + otherRecord.wins return Record(data) - + def get_standing_str(self): docstring = f"Wins: {self.wins} \nLosses: {self.losses} \nTies: {self.ties}" return docstring diff --git a/espn_api/hockey/team.py b/espn_api/hockey/team.py index dc0660ce4..1b791e679 100644 --- a/espn_api/hockey/team.py +++ b/espn_api/hockey/team.py @@ -4,57 +4,60 @@ class Team(object): - '''Teams are part of the league''' + """Teams are part of the league""" def __init__(self, data, roster, schedule, year, **kwargs): - self.team_id = data['id'] - self.team_abbrev = data['abbrev'] - self.team_name = data.get('name', 'Unknown') - if self.team_name == 'Unknown': - self.team_name = "%s %s" % (data.get('location', 'Unknown'), data.get('nickname', 'Unknown')) - self.division_id = data['divisionId'] - self.division_name = '' # set by caller - self.wins = data['record']['overall']['wins'] - self.losses = data['record']['overall']['losses'] - self.ties = data['record']['overall']['ties'] - self.owner = 'None' - self.logo_url = '' + self.team_id = data["id"] + self.team_abbrev = data["abbrev"] + self.team_name = data.get("name", "Unknown") + if self.team_name == "Unknown": + self.team_name = "%s %s" % ( + data.get("location", "Unknown"), + data.get("nickname", "Unknown"), + ) + self.division_id = data["divisionId"] + self.division_name = "" # set by caller + self.wins = data["record"]["overall"]["wins"] + self.losses = data["record"]["overall"]["losses"] + self.ties = data["record"]["overall"]["ties"] + self.owner = "None" + self.logo_url = "" self.stats = None - self.standing = data['playoffSeed'] - self.final_standing = data.get('rankFinal') or data.get('rankCalculatedFinal') + self.standing = data["playoffSeed"] + self.final_standing = data.get("rankFinal") or data.get("rankCalculatedFinal") self.roster = [] self.schedule = [] self.year = year - if 'valuesByStat' in data: - self.stats = {STATS_MAP[i]: j for i, j in data['valuesByStat'].items()} - if 'logo' in data: - self.logo_url = data['logo'] + if "valuesByStat" in data: + self.stats = {STATS_MAP[i]: j for i, j in data["valuesByStat"].items()} + if "logo" in data: + self.logo_url = data["logo"] self._fetch_roster(roster) self._fetch_schedule(schedule) - self.owners = kwargs.get('owners', []) + self.owners = kwargs.get("owners", []) def __repr__(self): - return 'Team(%s)' % (self.team_name,) + return "Team(%s)" % (self.team_name,) def _fetch_roster(self, data): - '''Fetch teams roster''' + """Fetch teams roster""" self.roster.clear() - roster = data['entries'] + roster = data["entries"] for player in roster: self.roster.append(Player(player)) def _fetch_schedule(self, data): - '''Fetch schedule and scores for team''' + """Fetch schedule and scores for team""" for match in data: - if 'away' in match.keys(): - if match['away']['teamId'] == self.team_id: + if "away" in match.keys(): + if match["away"]["teamId"] == self.team_id: new_match = Matchup(match) - setattr(new_match, 'away_team', self) + setattr(new_match, "away_team", self) self.schedule.append(new_match) - elif match['home']['teamId'] == self.team_id: + elif match["home"]["teamId"] == self.team_id: new_match = Matchup(match) - setattr(new_match, 'home_team', self) - self.schedule.append(new_match) \ No newline at end of file + setattr(new_match, "home_team", self) + self.schedule.append(new_match) diff --git a/espn_api/requests/__init__.py b/espn_api/requests/__init__.py index 64833fa85..6348192a4 100644 --- a/espn_api/requests/__init__.py +++ b/espn_api/requests/__init__.py @@ -1,3 +1,3 @@ -__all__ = ['EspnFantasyRequests'] +__all__ = ["EspnFantasyRequests"] -from .espn_requests import EspnFantasyRequests \ No newline at end of file +from .espn_requests import EspnFantasyRequests diff --git a/espn_api/requests/constant.py b/espn_api/requests/constant.py index 46f4b3e38..260b79cf9 100644 --- a/espn_api/requests/constant.py +++ b/espn_api/requests/constant.py @@ -1,9 +1,9 @@ -FANTASY_BASE_ENDPOINT = 'https://lm-api-reads.fantasy.espn.com/apis/v3/games/' -NEWS_BASE_ENDPOINT = 'https://site.api.espn.com/apis/fantasy/v3/games/' +FANTASY_BASE_ENDPOINT = "https://lm-api-reads.fantasy.espn.com/apis/v3/games/" +NEWS_BASE_ENDPOINT = "https://site.api.espn.com/apis/fantasy/v3/games/" FANTASY_SPORTS = { - 'nfl' : 'ffl', - 'nba' : 'fba', - 'nhl' : 'fhl', - 'mlb' : 'flb', - 'wnba' : 'wfba' -} \ No newline at end of file + "nfl": "ffl", + "nba": "fba", + "nhl": "fhl", + "mlb": "flb", + "wnba": "wfba", +} diff --git a/espn_api/requests/espn_requests.py b/espn_api/requests/espn_requests.py index c177979b0..796aef5c5 100644 --- a/espn_api/requests/espn_requests.py +++ b/espn_api/requests/espn_requests.py @@ -18,25 +18,44 @@ class ESPNUnknownError(Exception): class EspnFantasyRequests(object): - def __init__(self, sport: str, year: int, league_id: int, cookies: dict = None, logger: Logger = None): + def __init__( + self, + sport: str, + year: int, + league_id: int, + cookies: dict = None, + logger: Logger = None, + ): if sport not in FANTASY_SPORTS: - raise Exception(f'Unknown sport: {sport}, available options are {FANTASY_SPORTS.keys()}') + raise Exception( + f"Unknown sport: {sport}, available options are {FANTASY_SPORTS.keys()}" + ) self.year = year self.league_id = league_id - self.ENDPOINT = FANTASY_BASE_ENDPOINT + FANTASY_SPORTS[sport] + '/seasons/' + str(self.year) - self.NEWS_ENDPOINT = NEWS_BASE_ENDPOINT + FANTASY_SPORTS[sport] + '/news/' + 'players' + self.ENDPOINT = ( + FANTASY_BASE_ENDPOINT + FANTASY_SPORTS[sport] + "/seasons/" + str(self.year) + ) + self.NEWS_ENDPOINT = ( + NEWS_BASE_ENDPOINT + FANTASY_SPORTS[sport] + "/news/" + "players" + ) self.cookies = cookies self.logger = logger self.LEAGUE_ENDPOINT = FANTASY_BASE_ENDPOINT + FANTASY_SPORTS[sport] # older season data is stored at a different endpoint if year < 2018: - self.LEAGUE_ENDPOINT += "/leagueHistory/" + str(league_id) + "?seasonId=" + str(year) + self.LEAGUE_ENDPOINT += ( + "/leagueHistory/" + str(league_id) + "?seasonId=" + str(year) + ) else: - self.LEAGUE_ENDPOINT += "/seasons/" + str(year) + "/segments/0/leagues/" + str(league_id) - - def checkRequestStatus(self, status: int, extend: str = "", params: dict = None, headers: dict = None) -> dict: - '''Handles ESPN API response status codes and endpoint format switching''' + self.LEAGUE_ENDPOINT += ( + "/seasons/" + str(year) + "/segments/0/leagues/" + str(league_id) + ) + + def checkRequestStatus( + self, status: int, extend: str = "", params: dict = None, headers: dict = None + ) -> dict: + """Handles ESPN API response status codes and endpoint format switching""" if status == 401: # Try the alternate endpoint format, but save the original in case it fails original_endpoint = self.LEAGUE_ENDPOINT @@ -48,8 +67,13 @@ def checkRequestStatus(self, status: int, extend: str = "", params: dict = None, base_endpoint = self.LEAGUE_ENDPOINT.split(f"/seasons/")[0] self.LEAGUE_ENDPOINT = f"{base_endpoint}/leagueHistory/{self.league_id}?seasonId={self.year}" - #try the alternate endpoint - r = requests.get(self.LEAGUE_ENDPOINT + extend, params=params, headers=headers, cookies=self.cookies) + # try the alternate endpoint + r = requests.get( + self.LEAGUE_ENDPOINT + extend, + params=params, + headers=headers, + cookies=self.cookies, + ) if r.status_code == 200: # Return the updated response if alternate works @@ -59,10 +83,16 @@ def checkRequestStatus(self, status: int, extend: str = "", params: dict = None, self.LEAGUE_ENDPOINT = original_endpoint # If all endpoints failed, raise the corresponding error - if not self.cookies or 'espn_s2' not in self.cookies or 'SWID' not in self.cookies: + if ( + not self.cookies + or "espn_s2" not in self.cookies + or "SWID" not in self.cookies + ): raise ESPNAccessDenied("espn_s2 and swid are required") - raise ESPNAccessDenied(f"League {self.league_id} cannot be accessed with the provided credentials") + raise ESPNAccessDenied( + f"League {self.league_id} cannot be accessed with the provided credentials" + ) elif status == 404: raise ESPNInvalidLeague(f"League {self.league_id} does not exist") @@ -73,117 +103,132 @@ def checkRequestStatus(self, status: int, extend: str = "", params: dict = None, # If no issues with the status code, return None return None - def league_get(self, params: dict = None, headers: dict = None, extend: str = ''): + def league_get(self, params: dict = None, headers: dict = None, extend: str = ""): endpoint = self.LEAGUE_ENDPOINT + extend r = requests.get(endpoint, params=params, headers=headers, cookies=self.cookies) - alternate_response = self.checkRequestStatus(r.status_code, extend=extend, params=params, headers=headers) - + alternate_response = self.checkRequestStatus( + r.status_code, extend=extend, params=params, headers=headers + ) response = alternate_response if alternate_response else r.json() if self.logger: - self.logger.log_request(endpoint=self.LEAGUE_ENDPOINT + extend, params=params, headers=headers, response=response) + self.logger.log_request( + endpoint=self.LEAGUE_ENDPOINT + extend, + params=params, + headers=headers, + response=response, + ) return response[0] if isinstance(response, list) else response - def get(self, params: dict = None, headers: dict = None, extend: str = ''): + def get(self, params: dict = None, headers: dict = None, extend: str = ""): endpoint = self.ENDPOINT + extend r = requests.get(endpoint, params=params, headers=headers, cookies=self.cookies) self.checkRequestStatus(r.status_code) if self.logger: - self.logger.log_request(endpoint=endpoint, params=params, headers=headers, response=r.json()) + self.logger.log_request( + endpoint=endpoint, params=params, headers=headers, response=r.json() + ) return r.json() - def news_get(self, params: dict = None, headers: dict = None, extend: str = ''): + def news_get(self, params: dict = None, headers: dict = None, extend: str = ""): endpoint = self.NEWS_ENDPOINT + extend r = requests.get(endpoint, params=params, headers=headers, cookies=self.cookies) if self.logger: - self.logger.log_request(endpoint=endpoint, params=params, headers=headers, response=r.json()) + self.logger.log_request( + endpoint=endpoint, params=params, headers=headers, response=r.json() + ) return r.json() def get_league(self): - '''Gets all of the leagues initial data (teams, roster, matchups, settings)''' - params = { - 'view': ['mTeam', 'mRoster', 'mMatchup', 'mSettings', 'mStandings'] - } + """Gets all of the leagues initial data (teams, roster, matchups, settings)""" + params = {"view": ["mTeam", "mRoster", "mMatchup", "mSettings", "mStandings"]} data = self.league_get(params=params) return data def get_pro_schedule(self): - '''Gets the current sports professional team schedules''' - params = { - 'view': 'proTeamSchedules_wl' - } + """Gets the current sports professional team schedules""" + params = {"view": "proTeamSchedules_wl"} data = self.get(params=params) return data def get_pro_players(self): - '''Gets the current sports professional players''' - params = { - 'view': 'players_wl' - } + """Gets the current sports professional players""" + params = {"view": "players_wl"} filters = {"filterActive": {"value": True}} - headers = {'x-fantasy-filter': json.dumps(filters)} - data = self.get(extend='/players', params=params, headers=headers) + headers = {"x-fantasy-filter": json.dumps(filters)} + data = self.get(extend="/players", params=params, headers=headers) return data def get_league_draft(self): - '''Gets the leagues draft''' + """Gets the leagues draft""" params = { - 'view': 'mDraftDetail', + "view": "mDraftDetail", } data = self.league_get(params=params) return data - def get_league_message_board(self, msg_types = None): - '''Gets league message board and can filter by msg types''' - params = { - 'view': 'kona_league_messageboard' - } + def get_league_message_board(self, msg_types=None): + """Gets league message board and can filter by msg types""" + params = {"view": "kona_league_messageboard"} headers = None if msg_types is not None: - filters = { "topicsByType": {} } - base_filter = {"sortMessageDate":{"sortPriority":1,"sortAsc":False}} + filters = {"topicsByType": {}} + base_filter = {"sortMessageDate": {"sortPriority": 1, "sortAsc": False}} for msg_type in msg_types: - filters['topicsByType'][msg_type] = base_filter - headers = {'x-fantasy-filter': json.dumps(filters)} + filters["topicsByType"][msg_type] = base_filter + headers = {"x-fantasy-filter": json.dumps(filters)} - extend = "/segments/0/leagues/" + str(self.league_id) + '/communication' + extend = "/segments/0/leagues/" + str(self.league_id) + "/communication" data = self.get(params=params, extend=extend, headers=headers) return data def get_league_offers(self, week: int): - '''Gets the league offers reports''' - params = { - 'scoringPeriodId': week, - 'view': 'mTransactions2' - } + """Gets the league offers reports""" + params = {"scoringPeriodId": week, "view": "mTransactions2"} - filters = {"transactions": {"filterType": {"value": ["WAIVER", "WAIVER_ERROR"]}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = { + "transactions": {"filterType": {"value": ["WAIVER", "WAIVER_ERROR"]}} + } + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.league_get(params=params, headers=headers) return data - - def get_player_card(self, playerIds: List[int], max_scoring_period: int, additional_filters: List = None): - '''Gets the player card''' - params = { 'view': 'kona_playercard' } - additional_value = ["00{}".format(self.year), "10{}".format(self.year)] - if additional_filters : additional_value += additional_filters + def get_player_card( + self, + playerIds: List[int], + max_scoring_period: int, + additional_filters: List = None, + ): + """Gets the player card""" + params = {"view": "kona_playercard"} - filters = {'players':{'filterIds':{'value': playerIds}, 'filterStatsForTopScoringPeriodIds':{'value': max_scoring_period, 'additionalValue': additional_value}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + additional_value = ["00{}".format(self.year), "10{}".format(self.year)] + if additional_filters: + additional_value += additional_filters + + filters = { + "players": { + "filterIds": {"value": playerIds}, + "filterStatsForTopScoringPeriodIds": { + "value": max_scoring_period, + "additionalValue": additional_value, + }, + } + } + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.league_get(params=params, headers=headers) return data def get_player_news(self, playerId): - '''Gets the player news''' - params = {'playerId': playerId} + """Gets the player news""" + params = {"playerId": playerId} data = self.news_get(params=params) return data diff --git a/espn_api/utils/logger.py b/espn_api/utils/logger.py index b0f91dd25..9a906f7b6 100644 --- a/espn_api/utils/logger.py +++ b/espn_api/utils/logger.py @@ -2,6 +2,7 @@ import sys import json + class Logger(object): def __init__(self, name: str, debug=False): level = logging.DEBUG if debug else logging.INFO @@ -13,19 +14,20 @@ def __init__(self, name: str, debug=False): return handler = logging.StreamHandler(sys.stdout) - formatter = logging.Formatter('%(message)s') + formatter = logging.Formatter("%(message)s") handler.setFormatter(formatter) handler.setLevel(level) self.logging.addHandler(handler) self.logging.setLevel(level) - def log_request(self, endpoint: str, response: dict, params: dict = None, headers: dict = None): - log = f'ESPN API Request: url: {endpoint} params: {params} headers: {headers} \nESPN API Response: {json.dumps(response)}' + def log_request( + self, endpoint: str, response: dict, params: dict = None, headers: dict = None + ): + log = f"ESPN API Request: url: {endpoint} params: {params} headers: {headers} \nESPN API Response: {json.dumps(response)}" self.logging.debug(log) - # def setup_logger(debug=False) -> logging: # '''Setups Debug Logger''' # level = logging.DEBUG if debug else logging.INFO @@ -39,4 +41,3 @@ def log_request(self, endpoint: str, response: dict, params: dict = None, header # logger.addHandler(handler) # logger.setLevel(level) # return logger - diff --git a/espn_api/utils/utils.py b/espn_api/utils/utils.py index bd5690cfc..272bc873c 100644 --- a/espn_api/utils/utils.py +++ b/espn_api/utils/utils.py @@ -1,5 +1,6 @@ # Helper functions for json parsing and power rankings + def json_parsing(obj, key): """Recursively pull values of specified key from nested JSON.""" arr = [] @@ -8,7 +9,9 @@ def extract(obj, arr, key): """Return all matching values in an object.""" if isinstance(obj, dict): for k, v in obj.items(): - if isinstance(v, (dict)) or (isinstance(v, (list)) and v and isinstance(v[0], (list, dict))): + if isinstance(v, (dict)) or ( + isinstance(v, (list)) and v and isinstance(v[0], (list, dict)) + ): extract(v, arr, key) elif k == key: arr.append(v) diff --git a/espn_api/wbasketball/__init__.py b/espn_api/wbasketball/__init__.py index f36640171..b9baae89b 100644 --- a/espn_api/wbasketball/__init__.py +++ b/espn_api/wbasketball/__init__.py @@ -1,11 +1,11 @@ -__all__ = ['League', - 'Team', - 'Player', - 'Matchup', - ] +__all__ = [ + "League", + "Team", + "Player", + "Matchup", +] from .league import League from .team import Team from .player import Player from .matchup import Matchup - diff --git a/espn_api/wbasketball/activity.py b/espn_api/wbasketball/activity.py index af9a733c6..cc8ead641 100644 --- a/espn_api/wbasketball/activity.py +++ b/espn_api/wbasketball/activity.py @@ -1,30 +1,26 @@ from .constant import ACTIVITY_MAP + class Activity(object): def __init__(self, data, player_map, get_team_data): - self.actions = [] # List of tuples (Team, action, player) - self.date = data['date'] - for msg in data['messages']: - team = '' - action = 'UNKNOWN' - player = '' - msg_id = msg['messageTypeId'] + self.actions = [] # List of tuples (Team, action, player) + self.date = data["date"] + for msg in data["messages"]: + team = "" + action = "UNKNOWN" + player = "" + msg_id = msg["messageTypeId"] if msg_id == 244: - team = get_team_data(msg['from']) + team = get_team_data(msg["from"]) elif msg_id == 239: - team = get_team_data(msg['for']) + team = get_team_data(msg["for"]) else: - team = get_team_data(msg['to']) + team = get_team_data(msg["to"]) if msg_id in ACTIVITY_MAP: action = ACTIVITY_MAP[msg_id] - if msg['targetId'] in player_map: - player = player_map[msg['targetId']] + if msg["targetId"] in player_map: + player = player_map[msg["targetId"]] self.actions.append((team, action, player)) - - def __repr__(self): - return 'Activity(' + ' '.join("(%s,%s,%s)" % tup for tup in self.actions) + ')' - - - - + def __repr__(self): + return "Activity(" + " ".join("(%s,%s,%s)" % tup for tup in self.actions) + ")" diff --git a/espn_api/wbasketball/box_player.py b/espn_api/wbasketball/box_player.py index caf9e92dc..9b6acecb6 100644 --- a/espn_api/wbasketball/box_player.py +++ b/espn_api/wbasketball/box_player.py @@ -2,32 +2,43 @@ from .player import Player from datetime import datetime, timedelta + class BoxPlayer(Player): - '''player with extra data from a matchup''' + """player with extra data from a matchup""" + def __init__(self, data, pro_schedule, year): super(BoxPlayer, self).__init__(data, year) - self.slot_position = 'FA' - self.pro_opponent = "None" # professional team playing against - self.game_played = 100 # 0-100 for percent of game played + self.slot_position = "FA" + self.pro_opponent = "None" # professional team playing against + self.game_played = 100 # 0-100 for percent of game played self.points = 0 self.points_breakdown = {} - if 'lineupSlotId' in data: - self.slot_position = POSITION_MAP[data['lineupSlotId']] + if "lineupSlotId" in data: + self.slot_position = POSITION_MAP[data["lineupSlotId"]] - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] - if player['proTeamId'] in pro_schedule: - (opp_id, date) = pro_schedule[player['proTeamId']] - self.game_played = 100 if datetime.now() > datetime.fromtimestamp(date/1000.0) + timedelta(hours=3) else 0 + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) + if player["proTeamId"] in pro_schedule: + opp_id, date = pro_schedule[player["proTeamId"]] + self.game_played = ( + 100 + if datetime.now() + > datetime.fromtimestamp(date / 1000.0) + timedelta(hours=3) + else 0 + ) self.pro_opponent = PRO_TEAM_MAP[opp_id] - - player_stats = player.get('stats', []) + + player_stats = player.get("stats", []) for stats in player_stats: - stats_breakdown = stats.get('appliedStats') or stats.get('stats', {}) - breakdown = {STATS_MAP.get(k, k):v for (k,v) in stats_breakdown.items()} - points = round(stats.get('appliedTotal', 0), 2) + stats_breakdown = stats.get("appliedStats") or stats.get("stats", {}) + breakdown = {STATS_MAP.get(k, k): v for (k, v) in stats_breakdown.items()} + points = round(stats.get("appliedTotal", 0), 2) self.points = points self.points_breakdown = breakdown def __repr__(self): - return f'Player({self.name}, points:{self.points})' + return f"Player({self.name}, points:{self.points})" diff --git a/espn_api/wbasketball/box_score.py b/espn_api/wbasketball/box_score.py index 441e5946b..8289de4ec 100644 --- a/espn_api/wbasketball/box_score.py +++ b/espn_api/wbasketball/box_score.py @@ -1,37 +1,51 @@ from .box_player import BoxPlayer + class BoxScore(object): - ''' ''' + """ """ + def __init__(self, data, pro_schedule, by_matchup, year): - self.winner = data.get('winner', 'UNDECIDED') - self.home_team = data['home']['teamId'] - self.home_projected = -1 # week is over/not set - roster_key = 'rosterForMatchupPeriod' if by_matchup else 'rosterForCurrentScoringPeriod' + self.winner = data.get("winner", "UNDECIDED") + self.home_team = data["home"]["teamId"] + self.home_projected = -1 # week is over/not set + roster_key = ( + "rosterForMatchupPeriod" if by_matchup else "rosterForCurrentScoringPeriod" + ) # TODO combine home and away logic into common function - home_roster = data['home'].get(roster_key, {}) - if 'totalPointsLive' in data['home'] and by_matchup: - self.home_score = round(data['home']['totalPointsLive'], 2) - self.home_projected = round(data['home'].get('totalProjectedPointsLive', -1), 2) + home_roster = data["home"].get(roster_key, {}) + if "totalPointsLive" in data["home"] and by_matchup: + self.home_score = round(data["home"]["totalPointsLive"], 2) + self.home_projected = round( + data["home"].get("totalProjectedPointsLive", -1), 2 + ) else: - self.home_score = round(home_roster.get('appliedStatTotal', 0), 2) - self.home_lineup = [BoxPlayer(player, pro_schedule, year) for player in home_roster.get('entries', [])] + self.home_score = round(home_roster.get("appliedStatTotal", 0), 2) + self.home_lineup = [ + BoxPlayer(player, pro_schedule, year) + for player in home_roster.get("entries", []) + ] # For Leagues with bye weeks self.away_team = 0 self.away_score = 0 self.away_lineup = [] - self.away_projected = -1 # week is over/not set - if 'away' in data: - self.away_team = data['away']['teamId'] - away_roster = data['away'].get(roster_key, {}) - if 'totalPointsLive' in data['away'] and by_matchup: - self.away_score = round(data['away']['totalPointsLive'], 2) - self.away_projected = round(data['away'].get('totalProjectedPointsLive', -1), 2) - else: - self.away_score = round(away_roster.get('appliedStatTotal', 0), 2) - self.away_lineup = [BoxPlayer(player, pro_schedule, year) for player in away_roster.get('entries', [])] + self.away_projected = -1 # week is over/not set + if "away" in data: + self.away_team = data["away"]["teamId"] + away_roster = data["away"].get(roster_key, {}) + if "totalPointsLive" in data["away"] and by_matchup: + self.away_score = round(data["away"]["totalPointsLive"], 2) + self.away_projected = round( + data["away"].get("totalProjectedPointsLive", -1), 2 + ) + else: + self.away_score = round(away_roster.get("appliedStatTotal", 0), 2) + self.away_lineup = [ + BoxPlayer(player, pro_schedule, year) + for player in away_roster.get("entries", []) + ] def __repr__(self): away_team = self.away_team or "BYE" home_team = self.home_team or "BYE" - return f'Box Score({away_team} at {home_team})' + return f"Box Score({away_team} at {home_team})" diff --git a/espn_api/wbasketball/constant.py b/espn_api/wbasketball/constant.py index 6ba46a4f4..01603cac1 100644 --- a/espn_api/wbasketball/constant.py +++ b/espn_api/wbasketball/constant.py @@ -1,107 +1,107 @@ POSITION_MAP = { - 0: '', - 1: 'G', - 2: 'F', - 3: 'C', - 4: 'F/C', - 5: 'UTIL', - 6: 'BE', - 7: 'IR', - 8: 'Unknown', - 9: 'Unknown', + 0: "", + 1: "G", + 2: "F", + 3: "C", + 4: "F/C", + 5: "UTIL", + 6: "BE", + 7: "IR", + 8: "Unknown", + 9: "Unknown", # reverse - 'G': 1, - 'F': 2, - 'C': 3, - 'F/C': 4, - 'UTIL': 5, - 'BE': 6, - 'IR': 7, + "G": 1, + "F": 2, + "C": 3, + "F/C": 4, + "UTIL": 5, + "BE": 6, + "IR": 7, } PRO_TEAM_MAP = { - 0: 'FA', - 3: 'Dal', - 5: 'Ind', - 6: 'LA', - 8: 'Min', - 9: 'NY', - 11: 'Phx', - 14: 'Sea', - 16: 'Wsh', - 17: 'LV', - 18: 'Conn', - 19: 'Chi', - 20: 'Atl', - 129689: 'GSV', - 131935: 'Tor', - 132052: 'Por', + 0: "FA", + 3: "Dal", + 5: "Ind", + 6: "LA", + 8: "Min", + 9: "NY", + 11: "Phx", + 14: "Sea", + 16: "Wsh", + 17: "LV", + 18: "Conn", + 19: "Chi", + 20: "Atl", + 129689: "GSV", + 131935: "Tor", + 132052: "Por", } STATS_MAP = { - '0': 'PTS', - '1': 'BLK', - '2': 'STL', - '3': 'AST', - '4': 'OREB', - '5': 'DREB', - '6': 'REB', - '7': 'EJ', - '8': 'FF', - '9': 'PF', - '10': 'TF', - '11': 'TO', - '12': 'DQ', - '13': 'FGM', - '14': 'FGA', - '15': 'FTM', - '16': 'FTA', - '17': '3PM', - '18': '3PA', - '19': 'FG%', - '20': 'FT%', - '21': '3PT%', - '22': 'AFG%', - '23': 'FGMI', - '24': 'FTMI', - '25': '3PMI', - '26': 'APG', - '27': 'BPG', - '28': 'MPG', - '29': 'PPG', - '30': 'RPG', - '31': 'SPG', - '32': 'TOPG', - '33': '3PG', - '34': 'PPM', - '35': 'A/TO', - '36': 'STR', - '37': 'DD', - '38': 'TD', - '39': 'QD', - '40': 'MIN', - '41': 'GS', - '42': 'GP', - '43': 'TW', - '44': 'FTR', - '45': '45', + "0": "PTS", + "1": "BLK", + "2": "STL", + "3": "AST", + "4": "OREB", + "5": "DREB", + "6": "REB", + "7": "EJ", + "8": "FF", + "9": "PF", + "10": "TF", + "11": "TO", + "12": "DQ", + "13": "FGM", + "14": "FGA", + "15": "FTM", + "16": "FTA", + "17": "3PM", + "18": "3PA", + "19": "FG%", + "20": "FT%", + "21": "3PT%", + "22": "AFG%", + "23": "FGMI", + "24": "FTMI", + "25": "3PMI", + "26": "APG", + "27": "BPG", + "28": "MPG", + "29": "PPG", + "30": "RPG", + "31": "SPG", + "32": "TOPG", + "33": "3PG", + "34": "PPM", + "35": "A/TO", + "36": "STR", + "37": "DD", + "38": "TD", + "39": "QD", + "40": "MIN", + "41": "GS", + "42": "GP", + "43": "TW", + "44": "FTR", + "45": "45", } STAT_ID_MAP = { - '10': 'projected', - '01': 'last_7', - '02': 'last_15', - '03': 'last_30', + "10": "projected", + "01": "last_7", + "02": "last_15", + "03": "last_30", } ACTIVITY_MAP = { - 178: 'FA ADDED', - 180: 'WAIVER ADDED', - 179: 'DROPPED', - 181: 'DROPPED', - 239: 'DROPPED', - 244: 'TRADED', - 'FA': 178, - 'WAIVER': 180, - 'TRADED': 244, + 178: "FA ADDED", + 180: "WAIVER ADDED", + 179: "DROPPED", + 181: "DROPPED", + 239: "DROPPED", + 244: "TRADED", + "FA": 178, + "WAIVER": 180, + "TRADED": 244, } diff --git a/espn_api/wbasketball/league.py b/espn_api/wbasketball/league.py index 8b8275520..2723cb901 100644 --- a/espn_api/wbasketball/league.py +++ b/espn_api/wbasketball/league.py @@ -10,13 +10,30 @@ from .matchup import Matchup from .box_score import BoxScore from .constant import PRO_TEAM_MAP -from.activity import Activity +from .activity import Activity from .constant import POSITION_MAP, ACTIVITY_MAP + class League(BaseLeague): - '''Creates a League instance for Public/Private ESPN league''' - def __init__(self, league_id: int, year: int, espn_s2=None, swid=None, fetch_league=True, debug=False): - super().__init__(league_id=league_id, year=year, sport='wnba', espn_s2=espn_s2, swid=swid, debug=debug) + """Creates a League instance for Public/Private ESPN league""" + + def __init__( + self, + league_id: int, + year: int, + espn_s2=None, + swid=None, + fetch_league=True, + debug=False, + ): + super().__init__( + league_id=league_id, + year=year, + sport="wnba", + espn_s2=espn_s2, + swid=swid, + debug=debug, + ) if fetch_league: self.fetch_league() @@ -29,28 +46,31 @@ def fetch_league(self): def _fetch_league(self): data = super()._fetch_league() self._fetch_players() - self._map_matchup_ids(data['schedule']) - return(data) + self._map_matchup_ids(data["schedule"]) + return data def _map_matchup_ids(self, schedule): self.matchup_ids = {} for match in schedule: - matchup_period = match.get('matchupPeriodId') - scoring_periods = match.get('home', {}).get('pointsByScoringPeriod', {}).keys() + matchup_period = match.get("matchupPeriodId") + scoring_periods = ( + match.get("home", {}).get("pointsByScoringPeriod", {}).keys() + ) if len(scoring_periods) > 0: if matchup_period not in self.matchup_ids: self.matchup_ids[matchup_period] = sorted(scoring_periods) else: - self.matchup_ids[matchup_period] = sorted(set(self.matchup_ids[matchup_period] + list(scoring_periods))) - + self.matchup_ids[matchup_period] = sorted( + set(self.matchup_ids[matchup_period] + list(scoring_periods)) + ) def _fetch_teams(self, data): - '''Fetch teams in league''' + """Fetch teams in league""" super()._fetch_teams(data, TeamClass=Team) # replace opponentIds in schedule with team instances for team in self.teams: - team.division_name = self.settings.division_map.get(team.division_id, '') + team.division_name = self.settings.division_map.get(team.division_id, "") for week, matchup in enumerate(team.schedule): for opponent in self.teams: if matchup.away_team == opponent.team_id: @@ -58,23 +78,29 @@ def _fetch_teams(self, data): if matchup.home_team == opponent.team_id: matchup.home_team = opponent - - def standings(self) -> List[Team]: - standings = sorted(self.teams, key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, reverse=False) + standings = sorted( + self.teams, + key=lambda x: x.final_standing if x.final_standing != 0 else x.standing, + reverse=False, + ) return standings def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: - '''Returns list of matchups for a given matchup period''' + """Returns list of matchups for a given matchup period""" if not matchupPeriod: - matchupPeriod=self.currentMatchupPeriod + matchupPeriod = self.currentMatchupPeriod params = { - 'view': 'mMatchup', + "view": "mMatchup", } data = self.espn_request.league_get(params=params) - schedule = data['schedule'] - matchups = [Matchup(matchup) for matchup in schedule if matchup['matchupPeriodId'] == matchupPeriod] + schedule = data["schedule"] + matchups = [ + Matchup(matchup) + for matchup in schedule + if matchup["matchupPeriodId"] == matchupPeriod + ] for team in self.teams: for matchup in matchups: @@ -85,33 +111,52 @@ def scoreboard(self, matchupPeriod: int = None) -> List[Matchup]: return matchups - - def recent_activity(self, size: int = 25, msg_type: str = None, offset: int = 0) -> List[Activity]: - '''Returns a list of recent league activities (Add, Drop, Trade)''' + def recent_activity( + self, size: int = 25, msg_type: str = None, offset: int = 0 + ) -> List[Activity]: + """Returns a list of recent league activities (Add, Drop, Trade)""" if self.year < 2019: - raise Exception('Cant use recent activity before 2019') + raise Exception("Cant use recent activity before 2019") - msg_types = [178,180,179,239,181,244] + msg_types = [178, 180, 179, 239, 181, 244] if msg_type in ACTIVITY_MAP: msg_types = [ACTIVITY_MAP[msg_type]] - params = { - 'view': 'kona_league_communication' + params = {"view": "kona_league_communication"} + + filters = { + "topics": { + "filterType": {"value": ["ACTIVITY_TRANSACTIONS"]}, + "limit": size, + "limitPerMessageSet": {"value": 25}, + "offset": offset, + "sortMessageDate": {"sortPriority": 1, "sortAsc": False}, + "sortFor": {"sortPriority": 2, "sortAsc": False}, + "filterIncludeMessageTypeIds": {"value": msg_types}, + } } - - filters = {"topics":{"filterType":{"value":["ACTIVITY_TRANSACTIONS"]},"limit":size,"limitPerMessageSet":{"value":25},"offset":offset,"sortMessageDate":{"sortPriority":1,"sortAsc":False},"sortFor":{"sortPriority":2,"sortAsc":False},"filterIncludeMessageTypeIds":{"value":msg_types}}} - headers = {'x-fantasy-filter': json.dumps(filters)} - data = self.espn_request.league_get(extend='/communication/', params=params, headers=headers) - data = data['topics'] - activity = [Activity(topic, self.player_map, self.get_team_data) for topic in data] + headers = {"x-fantasy-filter": json.dumps(filters)} + data = self.espn_request.league_get( + extend="/communication/", params=params, headers=headers + ) + data = data["topics"] + activity = [ + Activity(topic, self.player_map, self.get_team_data) for topic in data + ] return activity - def free_agents(self, week: int=None, size: int=50, position: str=None, position_id: int=None) -> List[Player]: - '''Returns a List of Free Agents for a Given Week\n - Should only be used with most recent season''' + def free_agents( + self, + week: int = None, + size: int = 50, + position: str = None, + position_id: int = None, + ) -> List[Player]: + """Returns a List of Free Agents for a Given Week\n + Should only be used with most recent season""" if self.year < 2019: - raise Exception('Cant use free agents before 2019') + raise Exception("Cant use free agents before 2019") if not week: week = self.current_week @@ -121,23 +166,39 @@ def free_agents(self, week: int=None, size: int=50, position: str=None, position if position_id: slot_filter.append(position_id) - params = { - 'view': 'kona_player_info', - 'scoringPeriodId': week, + "view": "kona_player_info", + "scoringPeriodId": week, + } + filters = { + "players": { + "filterStatus": {"value": ["FREEAGENT", "WAIVERS"]}, + "filterSlotIds": {"value": slot_filter}, + "limit": size, + "sortPercOwned": {"sortPriority": 1, "sortAsc": False}, + "sortDraftRanks": { + "sortPriority": 100, + "sortAsc": True, + "value": "STANDARD", + }, + } } - filters = {"players":{"filterStatus":{"value":["FREEAGENT","WAIVERS"]},"filterSlotIds":{"value":slot_filter},"limit":size,"sortPercOwned":{"sortPriority":1,"sortAsc":False},"sortDraftRanks":{"sortPriority":100,"sortAsc":True,"value":"STANDARD"}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - players = data['players'] + players = data["players"] return [Player(player, self.year) for player in players] - def box_scores(self, matchup_period: int = None, scoring_period: int = None, matchup_total: bool = True) -> List[BoxScore]: - '''Returns list of box score for a given matchup or scoring period''' + def box_scores( + self, + matchup_period: int = None, + scoring_period: int = None, + matchup_total: bool = True, + ) -> List[BoxScore]: + """Returns list of box score for a given matchup or scoring period""" if self.year < 2019: - raise Exception('Cant use box score before 2019') + raise Exception("Cant use box score before 2019") matchup_id = self.currentMatchupPeriod scoring_id = self.current_week @@ -146,7 +207,11 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat scoring_id = scoring_period elif matchup_period and matchup_period < matchup_id: matchup_id = matchup_period - scoring_id = self.matchup_ids[matchup_period][-1] if matchup_period in self.matchup_ids else 1 + scoring_id = ( + self.matchup_ids[matchup_period][-1] + if matchup_period in self.matchup_ids + else 1 + ) elif scoring_period and scoring_period <= scoring_id: scoring_id = scoring_period for matchup in self.matchup_ids.keys(): @@ -155,17 +220,20 @@ def box_scores(self, matchup_period: int = None, scoring_period: int = None, mat break params = { - 'view': ['mMatchupScore', 'mScoreboard'], - 'scoringPeriodId': scoring_id + "view": ["mMatchupScore", "mScoreboard"], + "scoringPeriodId": scoring_id, } - filters = {"schedule":{"filterMatchupPeriodIds":{"value":[matchup_id]}}} - headers = {'x-fantasy-filter': json.dumps(filters)} + filters = {"schedule": {"filterMatchupPeriodIds": {"value": [matchup_id]}}} + headers = {"x-fantasy-filter": json.dumps(filters)} data = self.espn_request.league_get(params=params, headers=headers) - schedule = data['schedule'] + schedule = data["schedule"] pro_schedule = self._get_pro_schedule(scoring_id) - box_data = [BoxScore(matchup, pro_schedule, matchup_total, self.year) for matchup in schedule] + box_data = [ + BoxScore(matchup, pro_schedule, matchup_total, self.year) + for matchup in schedule + ] for team in self.teams: for matchup in box_data: diff --git a/espn_api/wbasketball/matchup.py b/espn_api/wbasketball/matchup.py index 4282dcddf..e24c4e562 100644 --- a/espn_api/wbasketball/matchup.py +++ b/espn_api/wbasketball/matchup.py @@ -1,7 +1,9 @@ from .constant import STATS_MAP + class Matchup(object): - '''Creates Matchup instance''' + """Creates Matchup instance""" + def __init__(self, data): self.home_team_live_score = None self.away_team_live_score = None @@ -12,31 +14,51 @@ def __repr__(self): # writing this too early to see if data['home']['totalPoints'] is final score # it might also be used for points leagues instead of category leagues if not self.away_team_live_score: - return f'Matchup({self.home_team}, {self.away_team})' + return f"Matchup({self.home_team}, {self.away_team})" else: - return f'Matchup({self.home_team} {round(self.home_team_live_score, 1)} - {round(self.away_team_live_score, 1)} {self.away_team})' + return f"Matchup({self.home_team} {round(self.home_team_live_score, 1)} - {round(self.away_team_live_score, 1)} {self.away_team})" def _fetch_matchup_info(self, data): - '''Fetch info for matchup''' - self.home_team = data['home']['teamId'] - self.home_final_score = data['home']['totalPoints'] - self.away_team = data['away']['teamId'] - self.away_final_score = data['away']['totalPoints'] - self.winner = data['winner'] + """Fetch info for matchup""" + self.home_team = data["home"]["teamId"] + self.home_final_score = data["home"]["totalPoints"] + self.away_team = data["away"]["teamId"] + self.away_final_score = data["away"]["totalPoints"] + self.winner = data["winner"] self.home_team_cats = None self.away_team_cats = None # if stats are available - if 'cumulativeScore' in data['home'].keys() and data['home']['cumulativeScore']['scoreByStat']: - - self.home_team_live_score = (data['home']['cumulativeScore']['wins'] + - data['home']['cumulativeScore']['ties']/2) - self.away_team_live_score = (data['away']['cumulativeScore']['wins'] + - data['away']['cumulativeScore']['ties']/2) + if ( + "cumulativeScore" in data["home"].keys() + and data["home"]["cumulativeScore"]["scoreByStat"] + ): - self.home_team_cats = { STATS_MAP[i]: {'score': data['home']['cumulativeScore']['scoreByStat'][i]['score'], - 'result': data['home']['cumulativeScore']['scoreByStat'][i]['result']} for i in data['home']['cumulativeScore']['scoreByStat'].keys()} + self.home_team_live_score = ( + data["home"]["cumulativeScore"]["wins"] + + data["home"]["cumulativeScore"]["ties"] / 2 + ) + self.away_team_live_score = ( + data["away"]["cumulativeScore"]["wins"] + + data["away"]["cumulativeScore"]["ties"] / 2 + ) - self.away_team_cats = { STATS_MAP[i]: {'score': data['away']['cumulativeScore']['scoreByStat'][i]['score'], - 'result': data['away']['cumulativeScore']['scoreByStat'][i]['result']} for i in data['away']['cumulativeScore']['scoreByStat'].keys()} + self.home_team_cats = { + STATS_MAP[i]: { + "score": data["home"]["cumulativeScore"]["scoreByStat"][i]["score"], + "result": data["home"]["cumulativeScore"]["scoreByStat"][i][ + "result" + ], + } + for i in data["home"]["cumulativeScore"]["scoreByStat"].keys() + } + self.away_team_cats = { + STATS_MAP[i]: { + "score": data["away"]["cumulativeScore"]["scoreByStat"][i]["score"], + "result": data["away"]["cumulativeScore"]["scoreByStat"][i][ + "result" + ], + } + for i in data["away"]["cumulativeScore"]["scoreByStat"].keys() + } diff --git a/espn_api/wbasketball/player.py b/espn_api/wbasketball/player.py index ae88b1c06..21c430313 100644 --- a/espn_api/wbasketball/player.py +++ b/espn_api/wbasketball/player.py @@ -1,45 +1,65 @@ from .constant import POSITION_MAP, PRO_TEAM_MAP, STATS_MAP, STAT_ID_MAP from espn_api.utils.utils import json_parsing + class Player(object): - '''Player are part of team''' + """Player are part of team""" + def __init__(self, data, year): - self.name = json_parsing(data, 'fullName') - self.playerId = json_parsing(data, 'id') - self.position = POSITION_MAP[json_parsing(data, 'defaultPositionId')] - self.lineupSlot = POSITION_MAP.get(data.get('lineupSlotId'), '') - self.eligibleSlots = [POSITION_MAP[pos] for pos in json_parsing(data, 'eligibleSlots')] - self.acquisitionType = json_parsing(data, 'acquisitionType') - self.proTeam = PRO_TEAM_MAP[json_parsing(data, 'proTeamId')] - self.injuryStatus = json_parsing(data, 'injuryStatus') + self.name = json_parsing(data, "fullName") + self.playerId = json_parsing(data, "id") + self.position = POSITION_MAP[json_parsing(data, "defaultPositionId")] + self.lineupSlot = POSITION_MAP.get(data.get("lineupSlotId"), "") + self.eligibleSlots = [ + POSITION_MAP[pos] for pos in json_parsing(data, "eligibleSlots") + ] + self.acquisitionType = json_parsing(data, "acquisitionType") + self.proTeam = PRO_TEAM_MAP[json_parsing(data, "proTeamId")] + self.injuryStatus = json_parsing(data, "injuryStatus") self.stats = {} # add available stats - player = data['playerPoolEntry']['player'] if 'playerPoolEntry' in data else data['player'] - self.injuryStatus = player.get('injuryStatus', self.injuryStatus) - self.injured = player.get('injured', False) + player = ( + data["playerPoolEntry"]["player"] + if "playerPoolEntry" in data + else data["player"] + ) + self.injuryStatus = player.get("injuryStatus", self.injuryStatus) + self.injured = player.get("injured", False) - for split in player.get('stats', []): - id = self._stat_id_pretty(split['id']) - applied_total = split.get('appliedTotal', 0) - applied_avg = round(split.get('appliedAverage', 0), 2) + for split in player.get("stats", []): + id = self._stat_id_pretty(split["id"]) + applied_total = split.get("appliedTotal", 0) + applied_avg = round(split.get("appliedAverage", 0), 2) self.stats[id] = dict(applied_total=applied_total, applied_avg=applied_avg) - if 'stats' in split: - if 'averageStats' in split.keys(): - self.stats[id]['avg'] = {STATS_MAP[i]: split['averageStats'][i] for i in split['averageStats'].keys() if STATS_MAP[i] != ''} - self.stats[id]['total'] = {STATS_MAP[i]: split['stats'][i] for i in split['stats'].keys() if STATS_MAP[i] != ''} + if "stats" in split: + if "averageStats" in split.keys(): + self.stats[id]["avg"] = { + STATS_MAP[i]: split["averageStats"][i] + for i in split["averageStats"].keys() + if STATS_MAP[i] != "" + } + self.stats[id]["total"] = { + STATS_MAP[i]: split["stats"][i] + for i in split["stats"].keys() + if STATS_MAP[i] != "" + } else: - self.stats[id]['avg'] = None - self.stats[id]['total'] = None - self.total_points = self.stats.get(f'{year}', {}).get('applied_total', 0) - self.avg_points = self.stats.get(f'{year}', {}).get('applied_avg', 0) - self.projected_total_points= self.stats.get(f'{year}_projected', {}).get('applied_total', 0) - self.projected_avg_points = self.stats.get(f'{year}_projected', {}).get('applied_avg', 0) - + self.stats[id]["avg"] = None + self.stats[id]["total"] = None + self.total_points = self.stats.get(f"{year}", {}).get("applied_total", 0) + self.avg_points = self.stats.get(f"{year}", {}).get("applied_avg", 0) + self.projected_total_points = self.stats.get(f"{year}_projected", {}).get( + "applied_total", 0 + ) + self.projected_avg_points = self.stats.get(f"{year}_projected", {}).get( + "applied_avg", 0 + ) + def __repr__(self): - return f'Player({self.name})' - + return f"Player({self.name})" + def _stat_id_pretty(self, id: str): id_type = STAT_ID_MAP.get(id[:2]) - return f'{id[2:]}_{id_type}' if id_type else id[2:] \ No newline at end of file + return f"{id[2:]}_{id_type}" if id_type else id[2:] diff --git a/espn_api/wbasketball/team.py b/espn_api/wbasketball/team.py index f47040aa4..678926405 100644 --- a/espn_api/wbasketball/team.py +++ b/espn_api/wbasketball/team.py @@ -2,58 +2,61 @@ from .matchup import Matchup from .constant import STATS_MAP + class Team(object): - '''Teams are part of the league''' + """Teams are part of the league""" + def __init__(self, data, roster, schedule, year, **kwargs): - self.team_id = data['id'] - self.team_abbrev = data['abbrev'] - self.team_name = data.get('name', 'Unknown') - if self.team_name == 'Unknown': - self.team_name = "%s %s" % (data.get('location', 'Unknown'), data.get('nickname', 'Unknown')) - self.division_id = data['divisionId'] - self.division_name = '' # set by caller - self.wins = data['record']['overall']['wins'] - self.losses = data['record']['overall']['losses'] - self.ties = data['record']['overall']['ties'] - self.owner = 'None' - self.logo_url = '' + self.team_id = data["id"] + self.team_abbrev = data["abbrev"] + self.team_name = data.get("name", "Unknown") + if self.team_name == "Unknown": + self.team_name = "%s %s" % ( + data.get("location", "Unknown"), + data.get("nickname", "Unknown"), + ) + self.division_id = data["divisionId"] + self.division_name = "" # set by caller + self.wins = data["record"]["overall"]["wins"] + self.losses = data["record"]["overall"]["losses"] + self.ties = data["record"]["overall"]["ties"] + self.owner = "None" + self.logo_url = "" self.stats = None - self.standing = data['playoffSeed'] - self.final_standing = data.get('rankFinal') or data.get('rankCalculatedFinal') + self.standing = data["playoffSeed"] + self.final_standing = data.get("rankFinal") or data.get("rankCalculatedFinal") self.roster = [] self.schedule = [] - - if 'valuesByStat' in data: - self.stats = {STATS_MAP[i]: j for i, j in data['valuesByStat'].items()} - if 'logo' in data: - self.logo_url = data['logo'] - + + if "valuesByStat" in data: + self.stats = {STATS_MAP[i]: j for i, j in data["valuesByStat"].items()} + if "logo" in data: + self.logo_url = data["logo"] + self._fetch_roster(roster, year) self._fetch_schedule(schedule) - self.owners = kwargs.get('owners', []) - + self.owners = kwargs.get("owners", []) + def __repr__(self): - return f'Team({self.team_name})' - + return f"Team({self.team_name})" def _fetch_roster(self, data, year): - '''Fetch teams roster''' + """Fetch teams roster""" self.roster.clear() - roster = data['entries'] + roster = data["entries"] for player in roster: self.roster.append(Player(player, year)) - def _fetch_schedule(self, data): - '''Fetch schedule and scores for team''' + """Fetch schedule and scores for team""" for match in data: - if 'away' in match.keys(): - if match['away']['teamId'] == self.team_id: + if "away" in match.keys(): + if match["away"]["teamId"] == self.team_id: new_match = Matchup(match) - setattr(new_match, 'away_team', self) + setattr(new_match, "away_team", self) self.schedule.append(new_match) - elif match['home']['teamId'] == self.team_id: + elif match["home"]["teamId"] == self.team_id: new_match = Matchup(match) - setattr(new_match, 'home_team', self) + setattr(new_match, "home_team", self) self.schedule.append(new_match) diff --git a/setup.py b/setup.py index f1b1c993d..9185d9b35 100644 --- a/setup.py +++ b/setup.py @@ -1,29 +1,32 @@ from setuptools import setup, find_packages pkg_vars = dict() -with open('espn_api/_version.py') as f: +with open("espn_api/_version.py") as f: exec(f.read(), pkg_vars) with open("README.md") as f: readme = f.read() setup( - name='espn_api', + name="espn_api", packages=find_packages(), version=pkg_vars["__version__"], - author='Christian Wendt', - description='ESPN API', + author="Christian Wendt", + description="ESPN API", long_description=readme, long_description_content_type="text/markdown", - install_requires=['requests>=2.32.4,<2.33.0', 'urllib3>=2.2.3,<2.3.0', 'idna>=3.12,<3.13'], - setup_requires=['nose>=1.0'], - test_suite='nose.collector', - tests_require=['nose', 'requests_mock', 'coverage'], - url='https://github.com/cwendt94/espn-api', + install_requires=[ + "requests>=2.32.4,<2.33.0", + "urllib3>=2.2.3,<2.3.0", + "idna>=3.12,<3.13", + ], + setup_requires=["nose>=1.0"], + test_suite="nose.collector", + tests_require=["nose", "requests_mock", "coverage"], + url="https://github.com/cwendt94/espn-api", classifiers=[ - 'Programming Language :: Python :: 3', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", ], - ) diff --git a/tests/baseball/integration/test_league.py b/tests/baseball/integration/test_league.py index 03f93c16a..a7ef09e1b 100644 --- a/tests/baseball/integration/test_league.py +++ b/tests/baseball/integration/test_league.py @@ -1,31 +1,32 @@ from unittest import TestCase from espn_api.baseball import League + # Integration test to make sure ESPN's API didnt change class LeagueTest(TestCase): def setUp(self): self.league = League(81134470, 2021) self.blank_league = League(81134470, 2021, fetch_league=False) - + def test_league_init(self): self.assertEqual(len(self.league.teams), 8) - # def test_league_scoreboard(self): - # league = League(81134470, 2021) - # scores = league.scoreboard() + # def test_league_scoreboard(self): + # league = League(81134470, 2021) + # scores = league.scoreboard() + + # self.assertEqual(scores[0].home_final_score, 4240.0) + # self.assertEqual(scores[0].away_final_score, 2965.0) - # self.assertEqual(scores[0].home_final_score, 4240.0) - # self.assertEqual(scores[0].away_final_score, 2965.0) - def test_league_free_agents(self): free_agents = self.league.free_agents() self.assertNotEqual(len(free_agents), 0) - + def test_league_box_scores(self): box_scores = self.league.box_scores(0) self.assertNotEqual(len(box_scores), 0) def test_blank_league_init(self): - self.assertEqual(len(self.blank_league.teams), 0) \ No newline at end of file + self.assertEqual(len(self.blank_league.teams), 0) diff --git a/tests/baseball/unit/test_box_score.py b/tests/baseball/unit/test_box_score.py index c3b375da4..413a05987 100644 --- a/tests/baseball/unit/test_box_score.py +++ b/tests/baseball/unit/test_box_score.py @@ -6,16 +6,24 @@ def _make_roto_team(team_id, total_points=50.0, stat_scores=None, entries=None): """Build a single team entry as returned by the ESPN ROTO schedule API.""" - stat_scores = stat_scores or {5: (10.0, 8.0), 20: (5.0, 6.0)} # stat_id: (score, rank) + stat_scores = stat_scores or { + 5: (10.0, 8.0), + 20: (5.0, 6.0), + } # stat_id: (score, rank) score_by_stat = { - str(stat_id): {'score': score, 'rank': rank, 'result': None, 'ineligible': False} + str(stat_id): { + "score": score, + "rank": rank, + "result": None, + "ineligible": False, + } for stat_id, (score, rank) in stat_scores.items() } return { - 'teamId': team_id, - 'totalPoints': total_points, - 'cumulativeScore': {'scoreByStat': score_by_stat}, - 'rosterForCurrentScoringPeriod': {'entries': entries or []}, + "teamId": team_id, + "totalPoints": total_points, + "cumulativeScore": {"scoreByStat": score_by_stat}, + "rosterForCurrentScoringPeriod": {"entries": entries or []}, } @@ -23,7 +31,7 @@ def _make_roto_data(matchup_period=1, teams=None): """Build a roto schedule entry (one per matchup period, all teams included).""" if teams is None: teams = [_make_roto_team(1), _make_roto_team(2, total_points=40.0)] - return {'matchupPeriodId': matchup_period, 'teams': teams} + return {"matchupPeriodId": matchup_period, "teams": teams} class RotoBoxScoreInitTest(TestCase): @@ -38,38 +46,38 @@ def test_teams_length(self): self.assertEqual(len(self.roto.teams), 2) def test_team_ids_stored(self): - ids = [e['team'] for e in self.roto.teams] + ids = [e["team"] for e in self.roto.teams] self.assertIn(1, ids) self.assertIn(2, ids) def test_total_points(self): - entry = next(e for e in self.roto.teams if e['team'] == 1) - self.assertEqual(entry['total_points'], 50.0) + entry = next(e for e in self.roto.teams if e["team"] == 1) + self.assertEqual(entry["total_points"], 50.0) def test_stats_dict_keyed_by_name(self): entry = self.roto.teams[0] # stat id 5 → 'HR', stat id 20 → 'R' per STATS_MAP - for stat_name in entry['stats']: + for stat_name in entry["stats"]: self.assertIsInstance(stat_name, str) def test_stat_has_score_and_rank(self): entry = self.roto.teams[0] - for stat in entry['stats'].values(): - self.assertIn('score', stat) - self.assertIn('rank', stat) + for stat in entry["stats"].values(): + self.assertIn("score", stat) + self.assertIn("rank", stat) def test_stat_values_correct(self): - entry = next(e for e in self.roto.teams if e['team'] == 1) + entry = next(e for e in self.roto.teams if e["team"] == 1) hr_name = STATS_MAP[5] - self.assertEqual(entry['stats'][hr_name]['score'], 10.0) - self.assertEqual(entry['stats'][hr_name]['rank'], 8.0) + self.assertEqual(entry["stats"][hr_name]["score"], 10.0) + self.assertEqual(entry["stats"][hr_name]["rank"], 8.0) def test_lineup_is_list(self): for entry in self.roto.teams: - self.assertIsInstance(entry['lineup'], list) + self.assertIsInstance(entry["lineup"], list) def test_repr(self): - self.assertEqual(repr(self.roto), 'Roto Box Score(period:1)') + self.assertEqual(repr(self.roto), "Roto Box Score(period:1)") def test_inherits_from_box_score(self): self.assertIsInstance(self.roto, BoxScore) @@ -85,34 +93,34 @@ def test_home_away_winner_are_none(self): class RotoBoxScoreTotalPointsLiveTest(TestCase): def test_live_score_takes_precedence(self): team = _make_roto_team(1, total_points=40.0) - team['totalPointsLive'] = 55.5 + team["totalPointsLive"] = 55.5 data = _make_roto_data(teams=[team]) roto = RotoBoxScore(data, pro_schedule={}, year=2026) - self.assertEqual(roto.teams[0]['total_points'], 55.5) + self.assertEqual(roto.teams[0]["total_points"], 55.5) def test_falls_back_to_total_points(self): team = _make_roto_team(1, total_points=40.0) # no totalPointsLive key data = _make_roto_data(teams=[team]) roto = RotoBoxScore(data, pro_schedule={}, year=2026) - self.assertEqual(roto.teams[0]['total_points'], 40.0) + self.assertEqual(roto.teams[0]["total_points"], 40.0) class RotoBoxScoreInfinityTest(TestCase): def test_infinity_string_converted_to_float_inf(self): - team = _make_roto_team(1, stat_scores={41: ('Infinity', 5.0)}) # 41 → WHIP + team = _make_roto_team(1, stat_scores={41: ("Infinity", 5.0)}) # 41 → WHIP data = _make_roto_data(teams=[team]) roto = RotoBoxScore(data, pro_schedule={}, year=2026) whip_name = STATS_MAP[41] - score = roto.teams[0]['stats'][whip_name]['score'] - self.assertEqual(score, float('inf')) + score = roto.teams[0]["stats"][whip_name]["score"] + self.assertEqual(score, float("inf")) def test_normal_score_unchanged(self): team = _make_roto_team(1, stat_scores={5: (12.0, 9.0)}) data = _make_roto_data(teams=[team]) roto = RotoBoxScore(data, pro_schedule={}, year=2026) hr_name = STATS_MAP[5] - self.assertEqual(roto.teams[0]['stats'][hr_name]['score'], 12.0) + self.assertEqual(roto.teams[0]["stats"][hr_name]["score"], 12.0) class RotoBoxScoreEmptyTeamsTest(TestCase): @@ -122,13 +130,13 @@ def test_empty_teams_list(self): self.assertEqual(roto.teams, []) def test_missing_teams_key(self): - data = {'matchupPeriodId': 1} + data = {"matchupPeriodId": 1} roto = RotoBoxScore(data, pro_schedule={}, year=2026) self.assertEqual(roto.teams, []) def test_missing_matchup_period(self): data = _make_roto_data(teams=[]) - del data['matchupPeriodId'] + del data["matchupPeriodId"] roto = RotoBoxScore(data, pro_schedule={}, year=2026) self.assertIsNone(roto.matchup_period) @@ -138,7 +146,7 @@ def test_process_team_noop(self): # _process_team is required by the abstract base but unused — calling it # directly should be a no-op and leave instance state untouched. roto = RotoBoxScore(_make_roto_data(teams=[]), pro_schedule={}, year=2026) - self.assertIsNone(roto._process_team({'teamId': 99}, True)) + self.assertIsNone(roto._process_team({"teamId": 99}, True)) self.assertIsNone(roto.home_team) self.assertIsNone(roto.away_team) @@ -146,11 +154,11 @@ def test_process_team_noop(self): class H2HPointsBoxScoreByeWeekTest(TestCase): def test_missing_away_returns_bye_defaults(self): data = { - 'winner': 'HOME', - 'home': { - 'teamId': 1, - 'totalPoints': 42.5, - 'rosterForCurrentScoringPeriod': {'entries': []}, + "winner": "HOME", + "home": { + "teamId": 1, + "totalPoints": 42.5, + "rosterForCurrentScoringPeriod": {"entries": []}, }, } box = H2HPointsBoxScore(data, pro_schedule={}, year=2026) diff --git a/tests/baseball/unit/test_league.py b/tests/baseball/unit/test_league.py index 628541369..85fe91cda 100644 --- a/tests/baseball/unit/test_league.py +++ b/tests/baseball/unit/test_league.py @@ -12,44 +12,44 @@ def _make_matchup_data(matchup_period_id=3, home_team_id=1, away_team_id=2): return { - 'matchupPeriodId': matchup_period_id, - 'home': {'teamId': home_team_id, 'totalPoints': 10.0}, - 'away': {'teamId': away_team_id, 'totalPoints': 8.0}, - 'winner': 'HOME', + "matchupPeriodId": matchup_period_id, + "home": {"teamId": home_team_id, "totalPoints": 10.0}, + "away": {"teamId": away_team_id, "totalPoints": 8.0}, + "winner": "HOME", } def _make_player_card_data(player_id=1001): return { - 'keeperValue': 5, - 'keeperValueFuture': 10, - 'lineupLocked': False, - 'rosterLocked': False, - 'tradeLocked': False, - 'onTeamId': 1, - 'player': { - 'fullName': 'Test Player', - 'id': player_id, - 'defaultPositionId': 1, - 'eligibleSlots': [0], - 'firstName': 'Test', - 'lastName': 'Player', - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'active': True, - 'droppable': True, - 'jersey': '42', - 'laterality': 'RIGHT', - 'stance': 'RIGHT', - 'lastNewsDate': None, - 'seasonOutlook': '', - 'proTeamId': 10, - 'ownership': { - 'percentOwned': 50.0, - 'percentStarted': 30.0, + "keeperValue": 5, + "keeperValueFuture": 10, + "lineupLocked": False, + "rosterLocked": False, + "tradeLocked": False, + "onTeamId": 1, + "player": { + "fullName": "Test Player", + "id": player_id, + "defaultPositionId": 1, + "eligibleSlots": [0], + "firstName": "Test", + "lastName": "Player", + "injuryStatus": "ACTIVE", + "injured": False, + "active": True, + "droppable": True, + "jersey": "42", + "laterality": "RIGHT", + "stance": "RIGHT", + "lastNewsDate": None, + "seasonOutlook": "", + "proTeamId": 10, + "ownership": { + "percentOwned": 50.0, + "percentStarted": 30.0, }, - 'draftRanksByRankType': {}, - 'stats': [], + "draftRanksByRankType": {}, + "stats": [], }, } @@ -58,56 +58,56 @@ class FreeAgentsPositionFilterTest(TestCase): """Tests that free_agents(position=...) correctly filters by slot ID.""" def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) self.league.current_week = 1 - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_position_string_builds_correct_slot_filter(self, mock_league_get): """Passing position='SP' should send slotId 14, not an empty filter.""" - mock_league_get.return_value = {'players': []} + mock_league_get.return_value = {"players": []} - self.league.free_agents(position='SP') + self.league.free_agents(position="SP") call_kwargs = mock_league_get.call_args - headers = call_kwargs.kwargs.get('headers') or call_kwargs[1].get('headers') - sent_filter = json.loads(headers['x-fantasy-filter']) - slot_ids = sent_filter['players']['filterSlotIds']['value'] + headers = call_kwargs.kwargs.get("headers") or call_kwargs[1].get("headers") + sent_filter = json.loads(headers["x-fantasy-filter"]) + slot_ids = sent_filter["players"]["filterSlotIds"]["value"] self.assertEqual(slot_ids, [14]) # 14 is the int key for 'SP' in POSITION_MAP - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_no_position_sends_empty_slot_filter(self, mock_league_get): """Calling free_agents() with no position should send an empty slot filter.""" - mock_league_get.return_value = {'players': []} + mock_league_get.return_value = {"players": []} self.league.free_agents() call_kwargs = mock_league_get.call_args - headers = call_kwargs.kwargs.get('headers') or call_kwargs[1].get('headers') - sent_filter = json.loads(headers['x-fantasy-filter']) - slot_ids = sent_filter['players']['filterSlotIds']['value'] + headers = call_kwargs.kwargs.get("headers") or call_kwargs[1].get("headers") + sent_filter = json.loads(headers["x-fantasy-filter"]) + slot_ids = sent_filter["players"]["filterSlotIds"]["value"] self.assertEqual(slot_ids, []) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_invalid_position_sends_empty_slot_filter(self, mock_league_get): """An unrecognized position string should not crash and should send an empty filter.""" - mock_league_get.return_value = {'players': []} + mock_league_get.return_value = {"players": []} - self.league.free_agents(position='INVALID') + self.league.free_agents(position="INVALID") call_kwargs = mock_league_get.call_args - headers = call_kwargs.kwargs.get('headers') or call_kwargs[1].get('headers') - sent_filter = json.loads(headers['x-fantasy-filter']) - slot_ids = sent_filter['players']['filterSlotIds']['value'] + headers = call_kwargs.kwargs.get("headers") or call_kwargs[1].get("headers") + sent_filter = json.loads(headers["x-fantasy-filter"]) + slot_ids = sent_filter["players"]["filterSlotIds"]["value"] self.assertEqual(slot_ids, []) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_all_position_strings_resolve_to_their_int_id(self, mock_league_get): """Every position name in POSITION_MAP should resolve to its correct int slot ID.""" - mock_league_get.return_value = {'players': []} + mock_league_get.return_value = {"players": []} for slot_id, pos_name in POSITION_MAP.items(): if not isinstance(slot_id, int): @@ -115,25 +115,27 @@ def test_all_position_strings_resolve_to_their_int_id(self, mock_league_get): with self.subTest(position=pos_name): self.league.free_agents(position=pos_name) call_kwargs = mock_league_get.call_args - headers = call_kwargs.kwargs.get('headers') or call_kwargs[1].get('headers') - sent_filter = json.loads(headers['x-fantasy-filter']) - slot_ids = sent_filter['players']['filterSlotIds']['value'] + headers = call_kwargs.kwargs.get("headers") or call_kwargs[1].get( + "headers" + ) + sent_filter = json.loads(headers["x-fantasy-filter"]) + slot_ids = sent_filter["players"]["filterSlotIds"]["value"] self.assertEqual(slot_ids, [slot_id]) class ScoreboardTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) self.league.currentMatchupPeriod = 3 self.mock_team = mock.Mock() self.mock_team.team_id = 1 self.league.teams = [self.mock_team] - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_returns_matchups_filtered_by_period(self, mock_get): mock_get.return_value = { - 'schedule': [ + "schedule": [ _make_matchup_data(matchup_period_id=3, home_team_id=1, away_team_id=2), _make_matchup_data(matchup_period_id=4, home_team_id=3, away_team_id=4), ] @@ -142,28 +144,32 @@ def test_returns_matchups_filtered_by_period(self, mock_get): self.assertEqual(len(result), 1) self.assertIsInstance(result[0], Matchup) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_explicit_matchup_period(self, mock_get): mock_get.return_value = { - 'schedule': [ + "schedule": [ _make_matchup_data(matchup_period_id=2, home_team_id=1, away_team_id=2), ] } result = self.league.scoreboard(matchupPeriod=2) self.assertEqual(len(result), 1) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_team_substituted_for_home(self, mock_get): mock_get.return_value = { - 'schedule': [_make_matchup_data(matchup_period_id=3, home_team_id=1, away_team_id=2)] + "schedule": [ + _make_matchup_data(matchup_period_id=3, home_team_id=1, away_team_id=2) + ] } result = self.league.scoreboard() self.assertEqual(result[0].home_team, self.mock_team) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_team_substituted_for_away(self, mock_get): mock_get.return_value = { - 'schedule': [_make_matchup_data(matchup_period_id=3, home_team_id=2, away_team_id=1)] + "schedule": [ + _make_matchup_data(matchup_period_id=3, home_team_id=2, away_team_id=1) + ] } result = self.league.scoreboard() self.assertEqual(result[0].away_team, self.mock_team) @@ -171,28 +177,26 @@ def test_team_substituted_for_away(self, mock_get): class RecentActivityTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) - self.league.player_map = {1001: 'Mike Trout'} + self.league.player_map = {1001: "Mike Trout"} mock_team = mock.Mock() mock_team.team_id = 1 self.league.teams = [mock_team] def test_raises_before_2019(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): old_league = League(league_id=1, year=2018) with self.assertRaises(Exception): old_league.recent_activity() - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_returns_activity_list(self, mock_get): mock_get.return_value = { - 'topics': [ + "topics": [ { - 'date': 1234567890000, - 'messages': [ - {'messageTypeId': 178, 'to': 1, 'targetId': 1001} - ], + "date": 1234567890000, + "messages": [{"messageTypeId": 178, "to": 1, "targetId": 1001}], } ] } @@ -200,72 +204,82 @@ def test_returns_activity_list(self, mock_get): self.assertEqual(len(result), 1) self.assertIsInstance(result[0], Activity) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_empty_topics_returns_empty_list(self, mock_get): - mock_get.return_value = {'topics': []} + mock_get.return_value = {"topics": []} result = self.league.recent_activity() self.assertEqual(result, []) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_msg_type_filter_applied(self, mock_get): - mock_get.return_value = {'topics': []} - self.league.recent_activity(msg_type='ADD') - headers_arg = mock_get.call_args.kwargs.get('headers') or mock_get.call_args[1].get('headers') - sent_filter = json.loads(headers_arg['x-fantasy-filter']) - self.assertIn('filterIncludeMessageTypeIds', sent_filter['topics']) + mock_get.return_value = {"topics": []} + self.league.recent_activity(msg_type="ADD") + headers_arg = mock_get.call_args.kwargs.get("headers") or mock_get.call_args[ + 1 + ].get("headers") + sent_filter = json.loads(headers_arg["x-fantasy-filter"]) + self.assertIn("filterIncludeMessageTypeIds", sent_filter["topics"]) class BoxScoresTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) self.league.currentMatchupPeriod = 3 self.league.current_week = 5 mock_team = mock.Mock() mock_team.team_id = 1 self.league.teams = [mock_team] - self.league._box_score_class = mock.Mock(return_value=mock.Mock(home_team=1, away_team=2)) + self.league._box_score_class = mock.Mock( + return_value=mock.Mock(home_team=1, away_team=2) + ) def test_raises_before_2019(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): old_league = League(league_id=1, year=2018) old_league._box_score_class = mock.Mock() with self.assertRaises(Exception): old_league.box_scores() - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_returns_box_score_list(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': [{'dummy': True}]} + mock_get.return_value = {"schedule": [{"dummy": True}]} result = self.league.box_scores() self.assertEqual(len(result), 1) - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_explicit_matchup_and_scoring_period(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': []} + mock_get.return_value = {"schedule": []} self.league.box_scores(matchup_period=2, scoring_period=10) - params = mock_get.call_args.kwargs.get('params') or mock_get.call_args[1].get('params') - self.assertEqual(params['scoringPeriodId'], 10) + params = mock_get.call_args.kwargs.get("params") or mock_get.call_args[1].get( + "params" + ) + self.assertEqual(params["scoringPeriodId"], 10) - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_matchup_period_only_uses_earlier_period(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': []} + mock_get.return_value = {"schedule": []} self.league.box_scores(matchup_period=1) - filters_header = mock_get.call_args.kwargs.get('headers') or mock_get.call_args[1].get('headers') - sent_filter = json.loads(filters_header['x-fantasy-filter']) - self.assertEqual(sent_filter['schedule']['filterMatchupPeriodIds']['value'], [1]) - - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + filters_header = mock_get.call_args.kwargs.get("headers") or mock_get.call_args[ + 1 + ].get("headers") + sent_filter = json.loads(filters_header["x-fantasy-filter"]) + self.assertEqual( + sent_filter["schedule"]["filterMatchupPeriodIds"]["value"], [1] + ) + + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_team_substituted_in_box_score(self, mock_get, mock_pro): mock_team = self.league.teams[0] mock_box = mock.Mock() mock_box.home_team = 1 mock_box.away_team = 2 self.league._box_score_class.return_value = mock_box - mock_get.return_value = {'schedule': [{'dummy': True}]} + mock_get.return_value = {"schedule": [{"dummy": True}]} self.league.box_scores() self.assertEqual(mock_box.home_team, mock_team) @@ -273,152 +287,167 @@ def test_team_substituted_in_box_score(self, mock_get, mock_pro): def _make_roto_schedule_entry(team_ids=(1, 2), matchup_period=1): teams = [ { - 'teamId': tid, - 'totalPoints': 50.0, - 'cumulativeScore': { - 'scoreByStat': { - '5': {'score': 10.0, 'rank': float(i + 1), 'result': None, 'ineligible': False}, + "teamId": tid, + "totalPoints": 50.0, + "cumulativeScore": { + "scoreByStat": { + "5": { + "score": 10.0, + "rank": float(i + 1), + "result": None, + "ineligible": False, + }, } }, - 'rosterForCurrentScoringPeriod': {'entries': []}, + "rosterForCurrentScoringPeriod": {"entries": []}, } for i, tid in enumerate(team_ids) ] - return {'matchupPeriodId': matchup_period, 'teams': teams} + return {"matchupPeriodId": matchup_period, "teams": teams} class BoxScoresRotoTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) self.league.currentMatchupPeriod = 1 self.league.current_week = 1 - self.league.scoring_type = 'ROTO' + self.league.scoring_type = "ROTO" self.league._box_score_class = RotoBoxScore mock_team = mock.Mock() mock_team.team_id = 1 self.league.teams = [mock_team] - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_returns_roto_box_score_instances(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': [_make_roto_schedule_entry()]} + mock_get.return_value = {"schedule": [_make_roto_schedule_entry()]} result = self.league.box_scores() self.assertEqual(len(result), 1) self.assertIsInstance(result[0], RotoBoxScore) - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_team_id_replaced_with_team_object(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': [_make_roto_schedule_entry(team_ids=(1, 2))]} + mock_get.return_value = { + "schedule": [_make_roto_schedule_entry(team_ids=(1, 2))] + } result = self.league.box_scores() roto = result[0] mock_team = self.league.teams[0] - matched = [e for e in roto.teams if e['team'] == mock_team] + matched = [e for e in roto.teams if e["team"] == mock_team] self.assertEqual(len(matched), 1) - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_unknown_team_id_left_as_int(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': [_make_roto_schedule_entry(team_ids=(1, 99))]} + mock_get.return_value = { + "schedule": [_make_roto_schedule_entry(team_ids=(1, 99))] + } result = self.league.box_scores() roto = result[0] - unmapped = [e for e in roto.teams if e['team'] == 99] + unmapped = [e for e in roto.teams if e["team"] == 99] self.assertEqual(len(unmapped), 1) - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_all_teams_present_in_snapshot(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': [_make_roto_schedule_entry(team_ids=(1, 2))]} + mock_get.return_value = { + "schedule": [_make_roto_schedule_entry(team_ids=(1, 2))] + } result = self.league.box_scores() self.assertEqual(len(result[0].teams), 2) - @mock.patch('espn_api.baseball.league.League._get_pro_schedule', return_value={}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch("espn_api.baseball.league.League._get_pro_schedule", return_value={}) + @mock.patch.object(EspnFantasyRequests, "league_get") def test_stats_and_rank_accessible(self, mock_get, mock_pro): - mock_get.return_value = {'schedule': [_make_roto_schedule_entry(team_ids=(1,))]} + mock_get.return_value = {"schedule": [_make_roto_schedule_entry(team_ids=(1,))]} result = self.league.box_scores() entry = result[0].teams[0] - self.assertIn('stats', entry) - for stat in entry['stats'].values(): - self.assertIn('score', stat) - self.assertIn('rank', stat) + self.assertIn("stats", entry) + for stat in entry["stats"].values(): + self.assertIn("score", stat) + self.assertIn("rank", stat) class PlayerInfoTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) - self.league.player_map = {'Mike Trout': 1001} + self.league.player_map = {"Mike Trout": 1001} self.league.finalScoringPeriod = 162 - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_by_name_returns_single_player(self, mock_get): - mock_get.return_value = {'players': [_make_player_card_data(1001)]} - result = self.league.player_info(name='Mike Trout') + mock_get.return_value = {"players": [_make_player_card_data(1001)]} + result = self.league.player_info(name="Mike Trout") self.assertIsInstance(result, Player) def test_by_name_not_found_returns_none(self): - result = self.league.player_info(name='Unknown Player') + result = self.league.player_info(name="Unknown Player") self.assertIsNone(result) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_unknown_name_does_not_clobber_explicit_player_id(self, mock_get): - mock_get.return_value = {'players': [_make_player_card_data(1001)]} - result = self.league.player_info(name='Unknown Player', playerId=1001) + mock_get.return_value = {"players": [_make_player_card_data(1001)]} + result = self.league.player_info(name="Unknown Player", playerId=1001) self.assertIsInstance(result, Player) def test_no_args_returns_none(self): result = self.league.player_info() self.assertIsNone(result) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_by_player_id_int_returns_single_player(self, mock_get): - mock_get.return_value = {'players': [_make_player_card_data(1001)]} + mock_get.return_value = {"players": [_make_player_card_data(1001)]} result = self.league.player_info(playerId=1001) self.assertIsInstance(result, Player) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_by_player_id_list_returns_list(self, mock_get): mock_get.return_value = { - 'players': [_make_player_card_data(1001), _make_player_card_data(1002)] + "players": [_make_player_card_data(1001), _make_player_card_data(1002)] } result = self.league.player_info(playerId=[1001, 1002]) self.assertIsInstance(result, list) self.assertEqual(len(result), 2) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_split_filters_included_in_request(self, mock_get): - mock_get.return_value = {'players': [_make_player_card_data(1001)]} + mock_get.return_value = {"players": [_make_player_card_data(1001)]} self.league.player_info(playerId=1001) - headers = mock_get.call_args.kwargs.get('headers') or mock_get.call_args[1].get('headers') - sent_filter = json.loads(headers['x-fantasy-filter']) - additional = sent_filter['players']['filterStatsForTopScoringPeriodIds']['additionalValue'] - self.assertIn('012021', additional) - self.assertIn('022021', additional) - self.assertIn('032021', additional) + headers = mock_get.call_args.kwargs.get("headers") or mock_get.call_args[1].get( + "headers" + ) + sent_filter = json.loads(headers["x-fantasy-filter"]) + additional = sent_filter["players"]["filterStatsForTopScoringPeriodIds"][ + "additionalValue" + ] + self.assertIn("012021", additional) + self.assertIn("022021", additional) + self.assertIn("032021", additional) class RefreshTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) self.league.teams = [] - @mock.patch.object(League, '_fetch_teams') - @mock.patch('espn_api.baseball.league.BaseLeague._fetch_league') + @mock.patch.object(League, "_fetch_teams") + @mock.patch("espn_api.baseball.league.BaseLeague._fetch_league") def test_refresh_updates_scoring_type(self, mock_fetch_league, mock_fetch_teams): mock_fetch_league.return_value = { - 'settings': {'scoringSettings': {'scoringType': 'H2H_CATEGORY'}} + "settings": {"scoringSettings": {"scoringType": "H2H_CATEGORY"}} } self.league.refresh() - self.assertEqual(self.league.scoring_type, 'H2H_CATEGORY') + self.assertEqual(self.league.scoring_type, "H2H_CATEGORY") - @mock.patch.object(League, '_fetch_teams') - @mock.patch('espn_api.baseball.league.BaseLeague._fetch_league') + @mock.patch.object(League, "_fetch_teams") + @mock.patch("espn_api.baseball.league.BaseLeague._fetch_league") def test_refresh_calls_fetch_teams(self, mock_fetch_league, mock_fetch_teams): mock_fetch_league.return_value = { - 'settings': {'scoringSettings': {'scoringType': 'H2H_CATEGORY'}} + "settings": {"scoringSettings": {"scoringType": "H2H_CATEGORY"}} } self.league.refresh() mock_fetch_teams.assert_called_once() @@ -426,26 +455,26 @@ def test_refresh_calls_fetch_teams(self, mock_fetch_league, mock_fetch_teams): class LoadRosterWeekTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) self.mock_team = mock.Mock() self.mock_team.team_id = 1 self.league.teams = [self.mock_team] - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_calls_fetch_roster_on_each_team(self, mock_get): - mock_get.return_value = { - 'teams': [{'id': 1, 'roster': {'entries': []}}] - } + mock_get.return_value = {"teams": [{"id": 1, "roster": {"entries": []}}]} self.league.load_roster_week(week=5) - self.mock_team._fetch_roster.assert_called_once_with({'entries': []}, 2021) + self.mock_team._fetch_roster.assert_called_once_with({"entries": []}, 2021) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_uses_correct_scoring_period(self, mock_get): - mock_get.return_value = {'teams': [{'id': 1, 'roster': {'entries': []}}]} + mock_get.return_value = {"teams": [{"id": 1, "roster": {"entries": []}}]} self.league.load_roster_week(week=7) - params = mock_get.call_args.kwargs.get('params') or mock_get.call_args[1].get('params') - self.assertEqual(params['scoringPeriodId'], 7) + params = mock_get.call_args.kwargs.get("params") or mock_get.call_args[1].get( + "params" + ) + self.assertEqual(params["scoringPeriodId"], 7) class StandingsTest(TestCase): @@ -457,7 +486,7 @@ def _make_team(self, team_id, final_standing, standing): return t def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) def test_sorted_by_final_standing(self): @@ -476,5 +505,7 @@ def test_zero_final_standing_falls_back_to_standing(self): self.assertEqual(result[0].team_id, 2) def test_returns_all_teams(self): - self.league.teams = [self._make_team(i, final_standing=i, standing=i) for i in range(1, 6)] + self.league.teams = [ + self._make_team(i, final_standing=i, standing=i) for i in range(1, 6) + ] self.assertEqual(len(self.league.standings()), 5) diff --git a/tests/baseball/unit/test_player.py b/tests/baseball/unit/test_player.py index ced1abf35..82b8da209 100644 --- a/tests/baseball/unit/test_player.py +++ b/tests/baseball/unit/test_player.py @@ -1,59 +1,83 @@ from datetime import datetime from unittest import TestCase -from espn_api.baseball.constant import DEFAULT_POSITION_MAP, POSITION_MAP, STAT_SPLIT_MAP +from espn_api.baseball.constant import ( + DEFAULT_POSITION_MAP, + POSITION_MAP, + STAT_SPLIT_MAP, +) from espn_api.baseball.player import Player -def _make_player_data(default_position_id=2, lineup_slot_id=0, eligible_slots=None, - ownership=None, pool_entry_extras=None, player_extras=None): +def _make_player_data( + default_position_id=2, + lineup_slot_id=0, + eligible_slots=None, + ownership=None, + pool_entry_extras=None, + player_extras=None, +): """Roster-style data: pool entry nested under 'playerPoolEntry'. Default position is C (catcher, ID=2).""" return { - 'defaultPositionId': default_position_id, - 'lineupSlotId': lineup_slot_id, - 'eligibleSlots': eligible_slots or [lineup_slot_id], - 'acquisitionType': 'DRAFT', - 'acquisitionDate': 1700000000000, - 'proTeamId': 10, - 'injuryStatus': 'ACTIVE', - 'status': 'ONTEAM', - 'playerPoolEntry': { - 'keeperValue': 5, - 'keeperValueFuture': 10, - 'lineupLocked': False, - 'rosterLocked': False, - 'tradeLocked': False, - 'onTeamId': 3, + "defaultPositionId": default_position_id, + "lineupSlotId": lineup_slot_id, + "eligibleSlots": eligible_slots or [lineup_slot_id], + "acquisitionType": "DRAFT", + "acquisitionDate": 1700000000000, + "proTeamId": 10, + "injuryStatus": "ACTIVE", + "status": "ONTEAM", + "playerPoolEntry": { + "keeperValue": 5, + "keeperValueFuture": 10, + "lineupLocked": False, + "rosterLocked": False, + "tradeLocked": False, + "onTeamId": 3, **(pool_entry_extras or {}), - 'player': { - 'fullName': 'Test Player', - 'id': 1234, - 'firstName': 'Test', - 'lastName': 'Player', - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'active': True, - 'droppable': True, - 'jersey': '42', - 'laterality': 'RIGHT', - 'stance': 'RIGHT', - 'lastNewsDate': 1700000000000, - 'seasonOutlook': 'Looking good.', - 'ownership': { - 'percentOwned': 50.0, - 'percentStarted': 30.0, - 'percentChange': 1.5, - 'averageDraftPosition': 100.0, - 'averageDraftPositionPercentChange': 0.5, - 'auctionValueAverage': 12.0, - 'auctionValueAverageChange': -1.0, + "player": { + "fullName": "Test Player", + "id": 1234, + "firstName": "Test", + "lastName": "Player", + "injuryStatus": "ACTIVE", + "injured": False, + "active": True, + "droppable": True, + "jersey": "42", + "laterality": "RIGHT", + "stance": "RIGHT", + "lastNewsDate": 1700000000000, + "seasonOutlook": "Looking good.", + "ownership": { + "percentOwned": 50.0, + "percentStarted": 30.0, + "percentChange": 1.5, + "averageDraftPosition": 100.0, + "averageDraftPositionPercentChange": 0.5, + "auctionValueAverage": 12.0, + "auctionValueAverageChange": -1.0, **(ownership or {}), }, - 'draftRanksByRankType': { - 'STANDARD': {'rank': 50, 'auctionValue': 20, 'rankSourceId': 0, 'rankType': 'STANDARD', 'slotId': 0, 'published': True}, - 'ROTO': {'rank': 45, 'auctionValue': 18, 'rankSourceId': 0, 'rankType': 'ROTO', 'slotId': 0, 'published': True}, + "draftRanksByRankType": { + "STANDARD": { + "rank": 50, + "auctionValue": 20, + "rankSourceId": 0, + "rankType": "STANDARD", + "slotId": 0, + "published": True, + }, + "ROTO": { + "rank": 45, + "auctionValue": 18, + "rankSourceId": 0, + "rankType": "ROTO", + "slotId": 0, + "published": True, + }, }, - 'stats': [], + "stats": [], **(player_extras or {}), }, }, @@ -63,42 +87,49 @@ def _make_player_data(default_position_id=2, lineup_slot_id=0, eligible_slots=No def _make_player_card_data(player_id=4424862): """Player card style: flat structure, no 'playerPoolEntry' wrapper.""" return { - 'keeperValue': 7, - 'keeperValueFuture': 14, - 'lineupLocked': True, - 'rosterLocked': False, - 'tradeLocked': True, - 'onTeamId': 5, - 'player': { - 'fullName': 'Card Player', - 'id': player_id, - 'defaultPositionId': 1, - 'eligibleSlots': [0], - 'firstName': 'Card', - 'lastName': 'Player', - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'active': True, - 'droppable': False, - 'jersey': '99', - 'laterality': 'LEFT', - 'stance': 'SWITCH', - 'lastNewsDate': 1700000000000, - 'seasonOutlook': 'Card outlook.', - 'proTeamId': 15, - 'ownership': { - 'percentOwned': 75.0, - 'percentStarted': 60.0, - 'percentChange': 2.0, - 'averageDraftPosition': 50.0, - 'averageDraftPositionPercentChange': 1.0, - 'auctionValueAverage': 25.0, - 'auctionValueAverageChange': 3.0, + "keeperValue": 7, + "keeperValueFuture": 14, + "lineupLocked": True, + "rosterLocked": False, + "tradeLocked": True, + "onTeamId": 5, + "player": { + "fullName": "Card Player", + "id": player_id, + "defaultPositionId": 1, + "eligibleSlots": [0], + "firstName": "Card", + "lastName": "Player", + "injuryStatus": "ACTIVE", + "injured": False, + "active": True, + "droppable": False, + "jersey": "99", + "laterality": "LEFT", + "stance": "SWITCH", + "lastNewsDate": 1700000000000, + "seasonOutlook": "Card outlook.", + "proTeamId": 15, + "ownership": { + "percentOwned": 75.0, + "percentStarted": 60.0, + "percentChange": 2.0, + "averageDraftPosition": 50.0, + "averageDraftPositionPercentChange": 1.0, + "auctionValueAverage": 25.0, + "auctionValueAverageChange": 3.0, }, - 'draftRanksByRankType': { - 'STANDARD': {'rank': 20, 'auctionValue': 30, 'rankSourceId': 0, 'rankType': 'STANDARD', 'slotId': 0, 'published': True}, + "draftRanksByRankType": { + "STANDARD": { + "rank": 20, + "auctionValue": 30, + "rankSourceId": 0, + "rankType": "STANDARD", + "slotId": 0, + "published": True, + }, }, - 'stats': [], + "stats": [], }, } @@ -111,13 +142,13 @@ def test_unknown_default_position_returns_string_id(self): """Unknown defaultPositionId should return a string of the ID, not crash.""" data = _make_player_data(default_position_id=99) player = Player(data, year=2021) - self.assertEqual(player.position, '99') + self.assertEqual(player.position, "99") def test_lineup_slot_uses_position_map(self): """lineupSlot should still use POSITION_MAP (lineup slot IDs).""" data = _make_player_data(default_position_id=1, lineup_slot_id=14) player = Player(data, year=2021) - self.assertEqual(player.lineupSlot, 'SP') + self.assertEqual(player.lineupSlot, "SP") def test_all_default_positions_covered(self): """Every entry in DEFAULT_POSITION_MAP should resolve correctly.""" @@ -133,35 +164,36 @@ def setUp(self): self.player = Player(_make_player_data(), year=2021) def test_basic_identity(self): - self.assertEqual(self.player.name, 'Test Player') - self.assertEqual(self.player.first_name, 'Test') - self.assertEqual(self.player.last_name, 'Player') + self.assertEqual(self.player.name, "Test Player") + self.assertEqual(self.player.first_name, "Test") + self.assertEqual(self.player.last_name, "Player") def test_active_and_droppable(self): self.assertTrue(self.player.active) self.assertTrue(self.player.droppable) def test_jersey(self): - self.assertEqual(self.player.jersey, '42') + self.assertEqual(self.player.jersey, "42") def test_laterality_and_stance(self): - self.assertEqual(self.player.laterality, 'RIGHT') - self.assertEqual(self.player.stance, 'RIGHT') + self.assertEqual(self.player.laterality, "RIGHT") + self.assertEqual(self.player.stance, "RIGHT") def test_last_news_date_is_datetime(self): self.assertIsInstance(self.player.last_news_date, datetime) def test_last_news_date_none_when_missing(self): data = _make_player_data() - del data['playerPoolEntry']['player']['lastNewsDate'] + del data["playerPoolEntry"]["player"]["lastNewsDate"] player = Player(data, year=2021) self.assertIsNone(player.last_news_date) def test_season_outlook(self): - self.assertEqual(self.player.season_outlook, 'Looking good.') + self.assertEqual(self.player.season_outlook, "Looking good.") def test_acquisition_date(self): from datetime import datetime + self.assertIsInstance(self.player.acquisitionDate, datetime) @@ -179,9 +211,13 @@ def test_lock_flags(self): self.assertFalse(self.player.trade_locked) def test_lock_flags_true(self): - data = _make_player_data(pool_entry_extras={ - 'lineupLocked': True, 'rosterLocked': True, 'tradeLocked': True, - }) + data = _make_player_data( + pool_entry_extras={ + "lineupLocked": True, + "rosterLocked": True, + "tradeLocked": True, + } + ) player = Player(data, year=2021) self.assertTrue(player.lineup_locked) self.assertTrue(player.roster_locked) @@ -215,14 +251,16 @@ def test_auction_value_change(self): self.assertAlmostEqual(self.player.auction_value_change, -1.0) def test_missing_ownership_fields_are_none(self): - data = _make_player_data(ownership={ - 'percentOwned': 10.0, - 'percentStarted': 5.0, - }) + data = _make_player_data( + ownership={ + "percentOwned": 10.0, + "percentStarted": 5.0, + } + ) # Remove the extra ownership keys by overriding entirely - data['playerPoolEntry']['player']['ownership'] = { - 'percentOwned': 10.0, - 'percentStarted': 5.0, + data["playerPoolEntry"]["player"]["ownership"] = { + "percentOwned": 10.0, + "percentStarted": 5.0, } player = Player(data, year=2021) self.assertIsNone(player.adp) @@ -237,16 +275,16 @@ def setUp(self): self.player = Player(_make_player_data(), year=2021) def test_draft_ranks_keys(self): - self.assertIn('STANDARD', self.player.draft_ranks) - self.assertIn('ROTO', self.player.draft_ranks) + self.assertIn("STANDARD", self.player.draft_ranks) + self.assertIn("ROTO", self.player.draft_ranks) def test_draft_ranks_values(self): - self.assertEqual(self.player.draft_ranks['STANDARD']['rank'], 50) - self.assertEqual(self.player.draft_ranks['STANDARD']['auction_value'], 20) - self.assertEqual(self.player.draft_ranks['ROTO']['rank'], 45) + self.assertEqual(self.player.draft_ranks["STANDARD"]["rank"], 50) + self.assertEqual(self.player.draft_ranks["STANDARD"]["auction_value"], 20) + self.assertEqual(self.player.draft_ranks["ROTO"]["rank"], 45) def test_empty_draft_ranks(self): - data = _make_player_data(player_extras={'draftRanksByRankType': {}}) + data = _make_player_data(player_extras={"draftRanksByRankType": {}}) player = Player(data, year=2021) self.assertEqual(player.draft_ranks, {}) @@ -258,7 +296,7 @@ def setUp(self): self.player = Player(_make_player_card_data(), year=2021) def test_name(self): - self.assertEqual(self.player.name, 'Card Player') + self.assertEqual(self.player.name, "Card Player") def test_keeper_values_from_top_level(self): self.assertEqual(self.player.keeper_value, 7) @@ -273,9 +311,9 @@ def test_on_team_id_from_top_level(self): self.assertEqual(self.player.on_team_id, 5) def test_jersey_laterality_stance(self): - self.assertEqual(self.player.jersey, '99') - self.assertEqual(self.player.laterality, 'LEFT') - self.assertEqual(self.player.stance, 'SWITCH') + self.assertEqual(self.player.jersey, "99") + self.assertEqual(self.player.laterality, "LEFT") + self.assertEqual(self.player.stance, "SWITCH") def test_adp_and_auction_value(self): self.assertAlmostEqual(self.player.adp, 50.0) @@ -284,25 +322,31 @@ def test_adp_and_auction_value(self): self.assertAlmostEqual(self.player.auction_value_change, 3.0) def test_draft_ranks(self): - self.assertIn('STANDARD', self.player.draft_ranks) - self.assertEqual(self.player.draft_ranks['STANDARD']['rank'], 20) - - -def _make_stat(split_type_id, scoring_period=0, season_id=2021, - stat_source_id=0, applied_total=10.0, stats=None): + self.assertIn("STANDARD", self.player.draft_ranks) + self.assertEqual(self.player.draft_ranks["STANDARD"]["rank"], 20) + + +def _make_stat( + split_type_id, + scoring_period=0, + season_id=2021, + stat_source_id=0, + applied_total=10.0, + stats=None, +): return { - 'statSplitTypeId': split_type_id, - 'scoringPeriodId': scoring_period, - 'seasonId': season_id, - 'statSourceId': stat_source_id, - 'appliedTotal': applied_total, - 'stats': stats or {'5': 1.0}, # stat key '5' = HR + "statSplitTypeId": split_type_id, + "scoringPeriodId": scoring_period, + "seasonId": season_id, + "statSourceId": stat_source_id, + "appliedTotal": applied_total, + "stats": stats or {"5": 1.0}, # stat key '5' = HR } class PlayerStatSplitsTest(TestCase): def _player_with_stats(self, stat_list): - data = _make_player_data(player_extras={'stats': stat_list}) + data = _make_player_data(player_extras={"stats": stat_list}) return Player(data, year=2021) def test_stats_splits_keys_present(self): @@ -312,58 +356,70 @@ def test_stats_splits_keys_present(self): def test_season_split_populates_stats(self): player = self._player_with_stats([_make_stat(0, applied_total=50.0)]) - self.assertIn(0, player.stats['season'] if 'season' in player.stats else player.stats) - self.assertIn(0, player.stats_splits['season']) - self.assertEqual(player.stats_splits['season'][0]['points'], 50.0) + self.assertIn( + 0, player.stats["season"] if "season" in player.stats else player.stats + ) + self.assertIn(0, player.stats_splits["season"]) + self.assertEqual(player.stats_splits["season"][0]["points"], 50.0) def test_season_split_also_in_stats_dict(self): """split_type=0 (season) must still appear in player.stats for backwards compat.""" player = self._player_with_stats([_make_stat(0, applied_total=30.0)]) self.assertIn(0, player.stats) - self.assertEqual(player.stats[0]['points'], 30.0) + self.assertEqual(player.stats[0]["points"], 30.0) def test_box_score_split_in_stats_dict(self): """split_type=5 (box_score) must still appear in player.stats.""" - player = self._player_with_stats([_make_stat(5, scoring_period=3, applied_total=7.0)]) + player = self._player_with_stats( + [_make_stat(5, scoring_period=3, applied_total=7.0)] + ) self.assertIn(3, player.stats) - self.assertEqual(player.stats[3]['points'], 7.0) + self.assertEqual(player.stats[3]["points"], 7.0) def test_last7_split_not_in_stats_dict(self): """split_type=1 (last_7) should NOT appear in player.stats.""" player = self._player_with_stats([_make_stat(1, applied_total=20.0)]) self.assertNotIn(0, player.stats) - self.assertIn(0, player.stats_splits['last_7']) + self.assertIn(0, player.stats_splits["last_7"]) def test_last15_and_last30_splits(self): - player = self._player_with_stats([ - _make_stat(2, applied_total=15.0), - _make_stat(3, applied_total=30.0), - ]) - self.assertEqual(player.stats_splits['last_15'][0]['points'], 15.0) - self.assertEqual(player.stats_splits['last_30'][0]['points'], 30.0) + player = self._player_with_stats( + [ + _make_stat(2, applied_total=15.0), + _make_stat(3, applied_total=30.0), + ] + ) + self.assertEqual(player.stats_splits["last_15"][0]["points"], 15.0) + self.assertEqual(player.stats_splits["last_30"][0]["points"], 30.0) def test_wrong_season_ignored(self): - player = self._player_with_stats([_make_stat(0, season_id=2020, applied_total=99.0)]) + player = self._player_with_stats( + [_make_stat(0, season_id=2020, applied_total=99.0)] + ) self.assertEqual(player.stats, {}) for split in player.stats_splits.values(): self.assertEqual(split, {}) def test_projected_split(self): - player = self._player_with_stats([_make_stat(0, stat_source_id=1, applied_total=25.0)]) - self.assertIn(0, player.stats_splits['season']) - self.assertEqual(player.stats_splits['season'][0]['projected_points'], 25.0) - self.assertNotIn('points', player.stats_splits['season'][0]) + player = self._player_with_stats( + [_make_stat(0, stat_source_id=1, applied_total=25.0)] + ) + self.assertIn(0, player.stats_splits["season"]) + self.assertEqual(player.stats_splits["season"][0]["projected_points"], 25.0) + self.assertNotIn("points", player.stats_splits["season"][0]) def test_breakdown_keys_mapped(self): - player = self._player_with_stats([_make_stat(0, stats={'5': 2.0})]) - self.assertEqual(player.stats_splits['season'][0]['breakdown']['HR'], 2.0) + player = self._player_with_stats([_make_stat(0, stats={"5": 2.0})]) + self.assertEqual(player.stats_splits["season"][0]["breakdown"]["HR"], 2.0) def test_total_points_from_season_stats(self): player = self._player_with_stats([_make_stat(0, applied_total=42.0)]) self.assertAlmostEqual(player.total_points, 42.0) def test_projected_total_points_from_projected_split(self): - player = self._player_with_stats([_make_stat(0, stat_source_id=1, applied_total=38.5)]) + player = self._player_with_stats( + [_make_stat(0, stat_source_id=1, applied_total=38.5)] + ) self.assertAlmostEqual(player.projected_total_points, 38.5) def test_total_points_zero_when_no_stats(self): @@ -372,51 +428,63 @@ def test_total_points_zero_when_no_stats(self): def test_batter_stats_filtered_for_pitchers(self): """Pitcher (SP, ID=1) should not include batter-only stats like HR (ID=5).""" - pitcher_data = _make_player_data(default_position_id=1, eligible_slots=[14], player_extras={'stats': [_make_stat(0, stats={'5': 2.0, '48': 10.0})]}) + pitcher_data = _make_player_data( + default_position_id=1, + eligible_slots=[14], + player_extras={"stats": [_make_stat(0, stats={"5": 2.0, "48": 10.0})]}, + ) pitcher = Player(pitcher_data, year=2021) - breakdown = pitcher.stats_splits['season'][0]['breakdown'] - self.assertNotIn('HR', breakdown) # HR is a batter-only stat - self.assertIn('K', breakdown) # K (strikeout) is a pitcher stat + breakdown = pitcher.stats_splits["season"][0]["breakdown"] + self.assertNotIn("HR", breakdown) # HR is a batter-only stat + self.assertIn("K", breakdown) # K (strikeout) is a pitcher stat def test_pitcher_stats_filtered_for_batters(self): """Batter (C, ID=2) should not include pitcher-only stats like ERA (ID=47).""" - batter_data = _make_player_data(default_position_id=2, eligible_slots=[0], player_extras={'stats': [_make_stat(0, stats={'5': 2.0, '47': 3.45})]}) + batter_data = _make_player_data( + default_position_id=2, + eligible_slots=[0], + player_extras={"stats": [_make_stat(0, stats={"5": 2.0, "47": 3.45})]}, + ) batter = Player(batter_data, year=2021) - breakdown = batter.stats_splits['season'][0]['breakdown'] - self.assertIn('HR', breakdown) # HR is a batter stat - self.assertNotIn('ERA', breakdown) # ERA is a pitcher-only stat + breakdown = batter.stats_splits["season"][0]["breakdown"] + self.assertIn("HR", breakdown) # HR is a batter stat + self.assertNotIn("ERA", breakdown) # ERA is a pitcher-only stat def test_multi_position_player_keeps_all_stats(self): """Ohtani-like player (eligible for both SP and DH) should keep both pitcher and batter stats.""" - multi_data = _make_player_data(eligible_slots=[0, 14], player_extras={'stats': [_make_stat(0, stats={'5': 2.0, '48': 10.0})]}) + multi_data = _make_player_data( + eligible_slots=[0, 14], + player_extras={"stats": [_make_stat(0, stats={"5": 2.0, "48": 10.0})]}, + ) multi = Player(multi_data, year=2021) - breakdown = multi.stats_splits['season'][0]['breakdown'] - self.assertIn('HR', breakdown) # HR is a batter stat - self.assertIn('K', breakdown) # K is a pitcher stat + breakdown = multi.stats_splits["season"][0]["breakdown"] + self.assertIn("HR", breakdown) # HR is a batter stat + self.assertIn("K", breakdown) # K is a pitcher stat class PlayerMiscTest(TestCase): def test_repr(self): player = Player(_make_player_data(), year=2021) - self.assertEqual(repr(player), 'Player(Test Player)') + self.assertEqual(repr(player), "Player(Test Player)") def test_eligible_slots_mapped(self): data = _make_player_data(eligible_slots=[0, 14]) player = Player(data, year=2021) - self.assertIn('C', player.eligibleSlots) - self.assertIn('SP', player.eligibleSlots) + self.assertIn("C", player.eligibleSlots) + self.assertIn("SP", player.eligibleSlots) def test_status_field(self): player = Player(_make_player_data(), year=2021) - self.assertEqual(player.status, 'ONTEAM') + self.assertEqual(player.status, "ONTEAM") def test_pro_team_mapped(self): from espn_api.baseball.constant import PRO_TEAM_MAP + for pro_id, pro_name in PRO_TEAM_MAP.items(): with self.subTest(pro_id=pro_id): - data = _make_player_data(player_extras={'proTeamId': pro_id}) + data = _make_player_data(player_extras={"proTeamId": pro_id}) # proTeamId in player_extras goes into player dict, but proTeam reads from # the outer roster entry's proTeamId — patch that instead - data['proTeamId'] = pro_id + data["proTeamId"] = pro_id player = Player(data, year=2021) self.assertEqual(player.proTeam, pro_name) diff --git a/tests/baseball/unit/test_settings.py b/tests/baseball/unit/test_settings.py index 8553fe611..87b22f6f3 100644 --- a/tests/baseball/unit/test_settings.py +++ b/tests/baseball/unit/test_settings.py @@ -4,66 +4,71 @@ from espn_api.baseball.settings import Settings -def _make_settings_data(lineup_slot_counts=None, scoring_type='H2H_CATEGORY', - scoring_enhancement_type='NONE'): +def _make_settings_data( + lineup_slot_counts=None, + scoring_type="H2H_CATEGORY", + scoring_enhancement_type="NONE", +): return { - 'name': 'Test League', - 'size': 10, - 'scoringSettings': { - 'scoringType': scoring_type, - 'scoringEnhancementType': scoring_enhancement_type, - 'matchupTieRule': 'NONE', - 'playoffMatchupTieRule': 'NONE', + "name": "Test League", + "size": 10, + "scoringSettings": { + "scoringType": scoring_type, + "scoringEnhancementType": scoring_enhancement_type, + "matchupTieRule": "NONE", + "playoffMatchupTieRule": "NONE", }, - 'scheduleSettings': { - 'matchupPeriodCount': 20, - 'matchupPeriods': {}, - 'playoffTeamCount': 4, - 'playoffMatchupPeriodLength': 1, - 'playoffSeedingRule': 'WINS', - 'divisions': [], + "scheduleSettings": { + "matchupPeriodCount": 20, + "matchupPeriods": {}, + "playoffTeamCount": 4, + "playoffMatchupPeriodLength": 1, + "playoffSeedingRule": "WINS", + "divisions": [], }, - 'tradeSettings': { - 'vetoVotesRequired': 4, - 'revisionHours': 48, + "tradeSettings": { + "vetoVotesRequired": 4, + "revisionHours": 48, }, - 'draftSettings': { - 'keeperCount': 0, + "draftSettings": { + "keeperCount": 0, }, - 'acquisitionSettings': { - 'isUsingAcquisitionBudget': True, - 'acquisitionBudget': 100, - 'acquisitionLimit': 50, - 'matchupAcquisitionLimit': 5, - 'matchupLimitPerScoringPeriod': True, - 'minimumBid': 1, - 'waiverProcessDays': ['MONDAY', 'THURSDAY'], - 'waiverProcessHour': 3, + "acquisitionSettings": { + "isUsingAcquisitionBudget": True, + "acquisitionBudget": 100, + "acquisitionLimit": 50, + "matchupAcquisitionLimit": 5, + "matchupLimitPerScoringPeriod": True, + "minimumBid": 1, + "waiverProcessDays": ["MONDAY", "THURSDAY"], + "waiverProcessHour": 3, }, - 'rosterSettings': { - 'lineupSlotCounts': lineup_slot_counts or {}, + "rosterSettings": { + "lineupSlotCounts": lineup_slot_counts or {}, }, } class SettingsPositionSlotCountsTest(TestCase): def test_known_slots_are_mapped(self): - data = _make_settings_data(lineup_slot_counts={ - '14': 2, # SP - '15': 3, # RP - '0': 1, # C - }) + data = _make_settings_data( + lineup_slot_counts={ + "14": 2, # SP + "15": 3, # RP + "0": 1, # C + } + ) settings = Settings(data) - self.assertEqual(settings.position_slot_counts['SP'], 2) - self.assertEqual(settings.position_slot_counts['RP'], 3) - self.assertEqual(settings.position_slot_counts['C'], 1) + self.assertEqual(settings.position_slot_counts["SP"], 2) + self.assertEqual(settings.position_slot_counts["RP"], 3) + self.assertEqual(settings.position_slot_counts["C"], 1) def test_unknown_slot_ids_are_excluded(self): """Slot IDs not in POSITION_MAP (e.g. 18, 21) should be silently dropped.""" - data = _make_settings_data(lineup_slot_counts={'18': 1, '14': 1}) + data = _make_settings_data(lineup_slot_counts={"18": 1, "14": 1}) settings = Settings(data) self.assertNotIn(18, settings.position_slot_counts) - self.assertIn('SP', settings.position_slot_counts) + self.assertIn("SP", settings.position_slot_counts) def test_empty_roster_settings(self): data = _make_settings_data(lineup_slot_counts={}) @@ -94,7 +99,7 @@ def test_minimum_bid(self): self.assertEqual(self.settings.minimum_bid, 1) def test_waiver_process_days(self): - self.assertEqual(self.settings.waiver_process_days, ['MONDAY', 'THURSDAY']) + self.assertEqual(self.settings.waiver_process_days, ["MONDAY", "THURSDAY"]) def test_waiver_process_hour(self): self.assertEqual(self.settings.waiver_process_hour, 3) @@ -104,8 +109,11 @@ def test_trade_revision_hours(self): def test_missing_acquisition_fields_default_to_none(self): data = _make_settings_data() - data['acquisitionSettings'] = {'isUsingAcquisitionBudget': False, 'acquisitionBudget': 0} - data['tradeSettings'] = {'vetoVotesRequired': 4} + data["acquisitionSettings"] = { + "isUsingAcquisitionBudget": False, + "acquisitionBudget": 0, + } + data["tradeSettings"] = {"vetoVotesRequired": 4} settings = Settings(data) self.assertIsNone(settings.acquisition_limit) self.assertIsNone(settings.matchup_acquisition_limit) @@ -119,7 +127,7 @@ def setUp(self): self.settings = Settings(_make_settings_data()) def test_name(self): - self.assertEqual(self.settings.name, 'Test League') + self.assertEqual(self.settings.name, "Test League") def test_team_count(self): self.assertEqual(self.settings.team_count, 10) @@ -140,34 +148,34 @@ def test_veto_votes_required(self): self.assertEqual(self.settings.veto_votes_required, 4) def test_scoring_type(self): - self.assertEqual(self.settings.scoring_type, 'H2H_CATEGORY') + self.assertEqual(self.settings.scoring_type, "H2H_CATEGORY") def test_median_scoring_false(self): self.assertFalse(self.settings.median_scoring) def test_median_scoring_true(self): - data = _make_settings_data(scoring_enhancement_type='WIN_BONUS_TOP_HALF') + data = _make_settings_data(scoring_enhancement_type="WIN_BONUS_TOP_HALF") settings = Settings(data) self.assertTrue(settings.median_scoring) def test_tie_rule(self): - self.assertEqual(self.settings.tie_rule, 'NONE') + self.assertEqual(self.settings.tie_rule, "NONE") def test_playoff_tie_rule(self): - self.assertEqual(self.settings.playoff_tie_rule, 'NONE') + self.assertEqual(self.settings.playoff_tie_rule, "NONE") def test_playoff_seed_tie_rule(self): - self.assertEqual(self.settings.playoff_seed_tie_rule, 'WINS') + self.assertEqual(self.settings.playoff_seed_tie_rule, "WINS") def test_repr(self): - self.assertEqual(repr(self.settings), 'Settings(Test League)') + self.assertEqual(repr(self.settings), "Settings(Test League)") def test_trade_deadline_zero_when_missing(self): self.assertEqual(self.settings.trade_deadline, 0) def test_trade_deadline_set_when_present(self): data = _make_settings_data() - data['tradeSettings']['deadlineDate'] = 1234567890 + data["tradeSettings"]["deadlineDate"] = 1234567890 settings = Settings(data) self.assertEqual(settings.trade_deadline, 1234567890) @@ -176,9 +184,9 @@ def test_division_map_empty_by_default(self): def test_division_map_populated(self): data = _make_settings_data() - data['scheduleSettings']['divisions'] = [ - {'id': 0, 'name': 'East'}, - {'id': 1, 'name': 'West'}, + data["scheduleSettings"]["divisions"] = [ + {"id": 0, "name": "East"}, + {"id": 1, "name": "West"}, ] settings = Settings(data) - self.assertEqual(settings.division_map, {0: 'East', 1: 'West'}) + self.assertEqual(settings.division_map, {0: "East", 1: "West"}) diff --git a/tests/baseball/unit/test_team.py b/tests/baseball/unit/test_team.py index c270d5f34..42e52dfbe 100644 --- a/tests/baseball/unit/test_team.py +++ b/tests/baseball/unit/test_team.py @@ -3,42 +3,55 @@ from espn_api.baseball.team import Team -def _make_record(wins=5, losses=3, ties=0, points_for=100.0, points_against=80.0, - streak_length=2, streak_type='WIN'): +def _make_record( + wins=5, + losses=3, + ties=0, + points_for=100.0, + points_against=80.0, + streak_length=2, + streak_type="WIN", +): return { - 'wins': wins, 'losses': losses, 'ties': ties, - 'pointsFor': points_for, 'pointsAgainst': points_against, - 'streakLength': streak_length, 'streakType': streak_type, - 'gamesBack': 0.0, 'percentage': 0.625, + "wins": wins, + "losses": losses, + "ties": ties, + "pointsFor": points_for, + "pointsAgainst": points_against, + "streakLength": streak_length, + "streakType": streak_type, + "gamesBack": 0.0, + "percentage": 0.625, } def _make_team_data(team_id=1, wins=5, losses=3): return { - 'id': team_id, - 'abbrev': 'TST', - 'name': 'Test Team', - 'divisionId': 0, - 'playoffSeed': 1, - 'rankCalculatedFinal': 1, - 'currentProjectedRank': 2, - 'waiverRank': 4, - 'points': 55.5, - 'record': { - 'overall': _make_record(wins=wins, losses=losses), - 'home': _make_record(wins=3, losses=1), - 'away': _make_record(wins=2, losses=2), - 'division': _make_record(wins=1, losses=1), + "id": team_id, + "abbrev": "TST", + "name": "Test Team", + "divisionId": 0, + "playoffSeed": 1, + "rankCalculatedFinal": 1, + "currentProjectedRank": 2, + "waiverRank": 4, + "points": 55.5, + "record": { + "overall": _make_record(wins=wins, losses=losses), + "home": _make_record(wins=3, losses=1), + "away": _make_record(wins=2, losses=2), + "division": _make_record(wins=1, losses=1), }, } def _make_team(data=None): data = data or _make_team_data() - roster = {'entries': []} + roster = {"entries": []} schedule = [] - with mock.patch('espn_api.baseball.team.Player'), \ - mock.patch('espn_api.baseball.team.Matchup'): + with mock.patch("espn_api.baseball.team.Player"), mock.patch( + "espn_api.baseball.team.Matchup" + ): return Team(data, roster, schedule, year=2026) @@ -57,7 +70,7 @@ def test_points_for_and_against(self): def test_streak(self): self.assertEqual(self.team.streak_length, 2) - self.assertEqual(self.team.streak_type, 'WIN') + self.assertEqual(self.team.streak_type, "WIN") def test_home_record(self): self.assertEqual(self.team.home_wins, 3) @@ -90,14 +103,14 @@ def test_points(self): def test_optional_fields_default_to_none(self): data = _make_team_data() - del data['currentProjectedRank'] - del data['waiverRank'] + del data["currentProjectedRank"] + del data["waiverRank"] team = _make_team(data) self.assertIsNone(team.current_projected_rank) self.assertIsNone(team.waiver_rank) def test_points_defaults_to_zero(self): data = _make_team_data() - del data['points'] + del data["points"] team = _make_team(data) self.assertEqual(team.points, 0) diff --git a/tests/baseball/unit/test_transaction.py b/tests/baseball/unit/test_transaction.py index b782f4a96..6598af97b 100644 --- a/tests/baseball/unit/test_transaction.py +++ b/tests/baseball/unit/test_transaction.py @@ -6,47 +6,55 @@ from espn_api.requests.espn_requests import EspnFantasyRequests -def _make_transaction_data(type_='FREEAGENT', status='EXECUTED', team_id=1, - scoring_period=1, player_id=1001, item_type='ADD', - is_pending=False): +def _make_transaction_data( + type_="FREEAGENT", + status="EXECUTED", + team_id=1, + scoring_period=1, + player_id=1001, + item_type="ADD", + is_pending=False, +): return { - 'teamId': team_id, - 'type': type_, - 'status': status, - 'isPending': is_pending, - 'scoringPeriodId': scoring_period, - 'processDate': 1234567890000, - 'bidAmount': None, - 'rating': 3, - 'executionType': 'PROCESS', - 'relatedTransactionId': None, - 'comment': '', - 'memberId': '{abc-123}', - 'items': [{ - 'type': item_type, - 'playerId': player_id, - 'fromTeamId': 0, - 'toTeamId': team_id, - 'fromLineupSlotId': -1, - 'toLineupSlotId': 16, - 'isKeeper': False, - 'overallPickNumber': None, - }], + "teamId": team_id, + "type": type_, + "status": status, + "isPending": is_pending, + "scoringPeriodId": scoring_period, + "processDate": 1234567890000, + "bidAmount": None, + "rating": 3, + "executionType": "PROCESS", + "relatedTransactionId": None, + "comment": "", + "memberId": "{abc-123}", + "items": [ + { + "type": item_type, + "playerId": player_id, + "fromTeamId": 0, + "toTeamId": team_id, + "fromLineupSlotId": -1, + "toLineupSlotId": 16, + "isKeeper": False, + "overallPickNumber": None, + } + ], } class TransactionClassTest(TestCase): def setUp(self): self.mock_team = mock.Mock() - self.mock_team.team_name = 'Test Team' - self.player_map = {1001: 'Mike Trout'} + self.mock_team.team_name = "Test Team" + self.player_map = {1001: "Mike Trout"} self.get_team_data = mock.Mock(return_value=self.mock_team) def test_basic_attributes(self): data = _make_transaction_data() t = Transaction(data, self.player_map, self.get_team_data) - self.assertEqual(t.type, 'FREEAGENT') - self.assertEqual(t.status, 'EXECUTED') + self.assertEqual(t.type, "FREEAGENT") + self.assertEqual(t.status, "EXECUTED") self.assertEqual(t.scoring_period, 1) self.assertFalse(t.is_pending) self.assertEqual(len(t.items), 1) @@ -57,30 +65,30 @@ def test_pending_true_from_api_field(self): self.assertTrue(t.is_pending) def test_pending_false_from_api_field(self): - data = _make_transaction_data(is_pending=False, status='PENDING') + data = _make_transaction_data(is_pending=False, status="PENDING") t = Transaction(data, self.player_map, self.get_team_data) self.assertFalse(t.is_pending) def test_pending_falls_back_to_status(self): - data = _make_transaction_data(status='PENDING') - del data['isPending'] + data = _make_transaction_data(status="PENDING") + del data["isPending"] t = Transaction(data, self.player_map, self.get_team_data) self.assertTrue(t.is_pending) def test_item_player_name_resolved(self): data = _make_transaction_data(player_id=1001) t = Transaction(data, self.player_map, self.get_team_data) - self.assertEqual(t.items[0].player_name, 'Mike Trout') + self.assertEqual(t.items[0].player_name, "Mike Trout") def test_item_unknown_player_name(self): data = _make_transaction_data(player_id=9999) t = Transaction(data, self.player_map, self.get_team_data) - self.assertEqual(t.items[0].player_name, 'Unknown') + self.assertEqual(t.items[0].player_name, "Unknown") def test_repr(self): data = _make_transaction_data() t = Transaction(data, self.player_map, self.get_team_data) - self.assertIn('FREEAGENT', repr(t)) + self.assertIn("FREEAGENT", repr(t)) def test_date_is_datetime(self): data = _make_transaction_data() @@ -89,14 +97,14 @@ def test_date_is_datetime(self): def test_date_falls_back_to_proposed(self): data = _make_transaction_data() - del data['processDate'] - data['proposedDate'] = 1234567890000 + del data["processDate"] + data["proposedDate"] = 1234567890000 t = Transaction(data, self.player_map, self.get_team_data) self.assertIsInstance(t.date, datetime) def test_date_none_when_missing(self): data = _make_transaction_data() - del data['processDate'] + del data["processDate"] t = Transaction(data, self.player_map, self.get_team_data) self.assertIsNone(t.date) @@ -104,11 +112,11 @@ def test_rating_and_execution_type(self): data = _make_transaction_data() t = Transaction(data, self.player_map, self.get_team_data) self.assertEqual(t.rating, 3) - self.assertEqual(t.execution_type, 'PROCESS') + self.assertEqual(t.execution_type, "PROCESS") def test_rating_defaults_to_none(self): data = _make_transaction_data() - del data['rating'] + del data["rating"] t = Transaction(data, self.player_map, self.get_team_data) self.assertIsNone(t.rating) @@ -125,87 +133,96 @@ def test_item_is_keeper(self): def test_item_is_keeper_true(self): data = _make_transaction_data() - data['items'][0]['isKeeper'] = True + data["items"][0]["isKeeper"] = True t = Transaction(data, self.player_map, self.get_team_data) self.assertTrue(t.items[0].is_keeper) def test_item_overall_pick_number(self): data = _make_transaction_data() - data['items'][0]['overallPickNumber'] = 42 + data["items"][0]["overallPickNumber"] = 42 t = Transaction(data, self.player_map, self.get_team_data) self.assertEqual(t.items[0].overall_pick_number, 42) class LeagueTransactionsTest(TestCase): def setUp(self): - with mock.patch.object(League, 'fetch_league'): + with mock.patch.object(League, "fetch_league"): self.league = League(league_id=1, year=2021) self.league.scoringPeriodId = 5 mock_team = mock.Mock() - mock_team.team_name = 'Test Team' + mock_team.team_name = "Test Team" mock_team.team_id = 1 self.league.teams = [mock_team] - self.league.player_map = {1001: 'Mike Trout'} + self.league.player_map = {1001: "Mike Trout"} - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_returns_transaction_list(self, mock_get): - mock_get.return_value = { - 'transactions': [_make_transaction_data()] - } + mock_get.return_value = {"transactions": [_make_transaction_data()]} result = self.league.transactions() self.assertEqual(len(result), 1) self.assertIsInstance(result[0], Transaction) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_empty_response(self, mock_get): mock_get.return_value = {} result = self.league.transactions() self.assertEqual(result, []) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_uses_current_scoring_period_by_default(self, mock_get): - mock_get.return_value = {'transactions': []} + mock_get.return_value = {"transactions": []} self.league.transactions() - params = mock_get.call_args.kwargs.get('params') or mock_get.call_args[1].get('params') - self.assertEqual(params['scoringPeriodId'], 5) + params = mock_get.call_args.kwargs.get("params") or mock_get.call_args[1].get( + "params" + ) + self.assertEqual(params["scoringPeriodId"], 5) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_explicit_scoring_period(self, mock_get): - mock_get.return_value = {'transactions': []} + mock_get.return_value = {"transactions": []} self.league.transactions(scoring_period=3) - params = mock_get.call_args.kwargs.get('params') or mock_get.call_args[1].get('params') - self.assertEqual(params['scoringPeriodId'], 3) + params = mock_get.call_args.kwargs.get("params") or mock_get.call_args[1].get( + "params" + ) + self.assertEqual(params["scoringPeriodId"], 3) def test_invalid_type_raises(self): with self.assertRaises(ValueError) as ctx: - self.league.transactions(types={'BOGUS_TYPE'}) - self.assertIn('BOGUS_TYPE', str(ctx.exception)) + self.league.transactions(types={"BOGUS_TYPE"}) + self.assertIn("BOGUS_TYPE", str(ctx.exception)) def test_valid_types_accepted(self): """All entries in TRANSACTION_TYPES should be accepted without error when mocked.""" - with mock.patch.object(EspnFantasyRequests, 'league_get', return_value={'transactions': []}): + with mock.patch.object( + EspnFantasyRequests, "league_get", return_value={"transactions": []} + ): for t in TRANSACTION_TYPES: with self.subTest(type=t): self.league.transactions(types={t}) - @mock.patch.object(EspnFantasyRequests, 'league_get') + @mock.patch.object(EspnFantasyRequests, "league_get") def test_types_filter_sent_in_header(self, mock_get): - mock_get.return_value = {'transactions': []} - self.league.transactions(types={'FREEAGENT'}) - headers = mock_get.call_args.kwargs.get('headers') or mock_get.call_args[1].get('headers') + mock_get.return_value = {"transactions": []} + self.league.transactions(types={"FREEAGENT"}) + headers = mock_get.call_args.kwargs.get("headers") or mock_get.call_args[1].get( + "headers" + ) import json - sent_filter = json.loads(headers['x-fantasy-filter']) - self.assertEqual(sent_filter['transactions']['filterType']['value'], ['FREEAGENT']) + + sent_filter = json.loads(headers["x-fantasy-filter"]) + self.assertEqual( + sent_filter["transactions"]["filterType"]["value"], ["FREEAGENT"] + ) class TransactionOptionalFieldsTest(TestCase): def setUp(self): - self.player_map = {1001: 'Mike Trout'} - self.get_team_data = mock.Mock(return_value=mock.Mock(team_name='Test Team')) + self.player_map = {1001: "Mike Trout"} + self.get_team_data = mock.Mock(return_value=mock.Mock(team_name="Test Team")) def test_bid_amount(self): data = _make_transaction_data() - data['bidAmount'] = 15 + data["bidAmount"] = 15 t = Transaction(data, self.player_map, self.get_team_data) self.assertEqual(t.bid_amount, 15) @@ -215,37 +232,41 @@ def test_bid_amount_none(self): def test_comment(self): data = _make_transaction_data() - data['comment'] = 'Picking up the best player' + data["comment"] = "Picking up the best player" t = Transaction(data, self.player_map, self.get_team_data) - self.assertEqual(t.comment, 'Picking up the best player') + self.assertEqual(t.comment, "Picking up the best player") def test_comment_defaults_to_none_when_missing(self): data = _make_transaction_data() - del data['comment'] + del data["comment"] t = Transaction(data, self.player_map, self.get_team_data) self.assertIsNone(t.comment) def test_member_id_defaults_to_none_when_missing(self): data = _make_transaction_data() - del data['memberId'] + del data["memberId"] t = Transaction(data, self.player_map, self.get_team_data) self.assertIsNone(t.member_id) def test_member_id(self): t = Transaction(_make_transaction_data(), self.player_map, self.get_team_data) - self.assertEqual(t.member_id, '{abc-123}') + self.assertEqual(t.member_id, "{abc-123}") def test_related_transaction_id_none(self): t = Transaction(_make_transaction_data(), self.player_map, self.get_team_data) self.assertIsNone(t.related_transaction_id) def test_team_resolved_via_callback(self): - mock_team = mock.Mock(team_name='Resolved Team') + mock_team = mock.Mock(team_name="Resolved Team") get_team = mock.Mock(return_value=mock_team) t = Transaction(_make_transaction_data(team_id=5), self.player_map, get_team) get_team.assert_called_once_with(5) - self.assertEqual(t.team.team_name, 'Resolved Team') + self.assertEqual(t.team.team_name, "Resolved Team") def test_item_repr(self): - t = Transaction(_make_transaction_data(item_type='ADD', player_id=1001), self.player_map, self.get_team_data) - self.assertEqual(repr(t.items[0]), 'ADD Mike Trout') + t = Transaction( + _make_transaction_data(item_type="ADD", player_id=1001), + self.player_map, + self.get_team_data, + ) + self.assertEqual(repr(t.items[0]), "ADD Mike Trout") diff --git a/tests/basketball/integration/test_league.py b/tests/basketball/integration/test_league.py index de137c045..b6b98030c 100644 --- a/tests/basketball/integration/test_league.py +++ b/tests/basketball/integration/test_league.py @@ -11,7 +11,7 @@ def setUp(self): def test_league_init(self): self.assertEqual(self.league.scoringPeriodId, 178) player = self.league.teams[0].roster[0] - self.assertEqual(player.schedule['2']['team'], 'BKN') + self.assertEqual(player.schedule["2"]["team"], "BKN") self.assertEqual(player.total_points, 3583.0) self.assertEqual(player.avg_points, 45.35) @@ -24,10 +24,10 @@ def test_league_scoreboard(self): def test_league_draft(self): draft = self.league.draft - self.assertEqual(draft[1].playerName, 'LeBron James') + self.assertEqual(draft[1].playerName, "LeBron James") self.assertEqual(draft[1].round_num, 1) self.assertEqual(draft[2].round_pick, 3) - self.assertEqual(draft[2].team.team_name, 'Denver Nuggets ') + self.assertEqual(draft[2].team.team_name, "Denver Nuggets ") def test_league_free_agents(self): free_agents = self.league.free_agents() @@ -39,21 +39,22 @@ def test_player_info(self): player = self.league.player_info(playerId=player_id) - self.assertEqual(player.__repr__(), 'Player(Andre Drummond)') - self.assertEqual(player.schedule['2']['team'], 'BKN') - self.assertEqual(player.stats['2']['team'], 'BKN') - self.assertEqual(player.stats['2']['total']['PTS'], 24.0) - self.assertEqual(player.nine_cat_averages, + self.assertEqual(player.__repr__(), "Player(Andre Drummond)") + self.assertEqual(player.schedule["2"]["team"], "BKN") + self.assertEqual(player.stats["2"]["team"], "BKN") + self.assertEqual(player.stats["2"]["total"]["PTS"], 24.0) + self.assertEqual( + player.nine_cat_averages, { - 'PTS': 17.3, - 'BLK': 1.7, - 'STL': 1.7, - 'AST': 1.4, - 'REB': 15.6, - 'TO': 2.2, - '3PM': 0.1, - 'FG%': 0.533, - 'FT%': 0.59, + "PTS": 17.3, + "BLK": 1.7, + "STL": 1.7, + "AST": 1.4, + "REB": 15.6, + "TO": 2.2, + "3PM": 0.1, + "FG%": 0.533, + "FT%": 0.59, }, ) @@ -61,7 +62,9 @@ def test_league_box_scores(self): final_matchup = self.league.box_scores()[0] middle_matchup = self.league.box_scores(matchup_period=7)[0] # same matchup period but single scoring period - scoring_period_matchup = self.league.box_scores(scoring_period=48, matchup_total=False)[0] + scoring_period_matchup = self.league.box_scores( + scoring_period=48, matchup_total=False + )[0] self.assertEqual(final_matchup.home_score, 4240.0) self.assertEqual(final_matchup.away_lineup[0].points, 156.0) @@ -77,10 +80,12 @@ def test_league_box_scores_category(self): score = league.box_scores(matchup_period=3, scoring_period=21) - self.assertEqual(score[0].__repr__(), 'Box Score(Team(Team McWilliams) at Team(Team Wendt))') - self.assertEqual(score[0].away_lineup[0].name, 'Stephen Curry') + self.assertEqual( + score[0].__repr__(), "Box Score(Team(Team McWilliams) at Team(Team Wendt))" + ) + self.assertEqual(score[0].away_lineup[0].name, "Stephen Curry") # comment for now until matchup week is over - self.assertEqual(score[0].away_stats['PTS'], { 'value': 733.0, 'result': 'WIN' }) + self.assertEqual(score[0].away_stats["PTS"], {"value": 733.0, "result": "WIN"}) def test_past_league(self): league = League(411647, 2017) diff --git a/tests/basketball/unit/test_activity.py b/tests/basketball/unit/test_activity.py index 30e18cd02..95b990a17 100644 --- a/tests/basketball/unit/test_activity.py +++ b/tests/basketball/unit/test_activity.py @@ -7,307 +7,307 @@ class ActivityTest(TestCase): def setUp(self): """Set up test fixtures""" self.player_map = { - 1001: 'Player One', - 1002: 'Player Two', - 1003: 'Player Three', + 1001: "Player One", + 1002: "Player Two", + 1003: "Player Three", } - + self.team_data = { - 'team1': 'Team One', - 'team2': 'Team Two', - 'team3': 'Team Three', + "team1": "Team One", + "team2": "Team Two", + "team3": "Team Three", } - - self.get_team_data = lambda team_id: self.team_data.get(team_id, '') + + self.get_team_data = lambda team_id: self.team_data.get(team_id, "") def test_activity_init_empty_messages(self): """Test Activity initialization with empty messages""" - data = { - 'date': '2023-01-01', - 'messages': [] - } - + data = {"date": "2023-01-01", "messages": []} + activity = Activity(data, self.player_map, self.get_team_data) - - self.assertEqual(activity.date, '2023-01-01') + + self.assertEqual(activity.date, "2023-01-01") self.assertEqual(len(activity.actions), 0) def test_activity_init_with_fa_added(self): """Test Activity with FA_ADDED message (type 178)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(team, 'Team One') - self.assertEqual(action, 'FA ADDED') - self.assertEqual(player, 'Player One') - self.assertEqual(position, '') + self.assertEqual(team, "Team One") + self.assertEqual(action, "FA ADDED") + self.assertEqual(player, "Player One") + self.assertEqual(position, "") def test_activity_init_with_waiver_added(self): """Test Activity with WAIVER_ADDED message (type 180)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 180, - 'to': 'team1', - 'targetId': 1002, + "messageTypeId": 180, + "to": "team1", + "targetId": 1002, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(team, 'Team One') - self.assertEqual(action, 'WAIVER ADDED') - self.assertEqual(player, 'Player Two') + self.assertEqual(team, "Team One") + self.assertEqual(action, "WAIVER ADDED") + self.assertEqual(player, "Player Two") def test_activity_init_with_dropped(self): """Test Activity with DROPPED message (type 179)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 179, - 'to': 'team2', - 'targetId': 1001, + "messageTypeId": 179, + "to": "team2", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(team, 'Team Two') - self.assertEqual(action, 'DROPPED') - self.assertEqual(player, 'Player One') + self.assertEqual(team, "Team Two") + self.assertEqual(action, "DROPPED") + self.assertEqual(player, "Player One") def test_activity_init_with_traded(self): """Test Activity with TRADED message (type 244)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 244, - 'from': 'team1', - 'targetId': 1003, + "messageTypeId": 244, + "from": "team1", + "targetId": 1003, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(team, 'Team One') - self.assertEqual(action, 'TRADED') - self.assertEqual(player, 'Player Three') + self.assertEqual(team, "Team One") + self.assertEqual(action, "TRADED") + self.assertEqual(player, "Player Three") def test_activity_init_with_moved_no_include(self): """Test Activity with MOVED message (type 188) when include_moved=False""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 188, - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 188, + "to": "team1", + "targetId": 1001, } - ] + ], } - - activity = Activity(data, self.player_map, self.get_team_data, include_moved=False) - + + activity = Activity( + data, self.player_map, self.get_team_data, include_moved=False + ) + # With include_moved=False and type 188, action should be UNKNOWN self.assertEqual(len(activity.actions), 0) def test_activity_init_with_moved_include(self): """Test Activity with MOVED message (type 188) when include_moved=True""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 188, - 'to': 4, # Position ID that's in POSITION_MAP - 'targetId': 1001, + "messageTypeId": 188, + "to": 4, # Position ID that's in POSITION_MAP + "targetId": 1001, } - ] + ], } - - activity = Activity(data, self.player_map, self.get_team_data, include_moved=True) - + + activity = Activity( + data, self.player_map, self.get_team_data, include_moved=True + ) + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(action, 'MOVED') - self.assertEqual(position, 'C') # Position ID 4 maps to 'C' + self.assertEqual(action, "MOVED") + self.assertEqual(position, "C") # Position ID 4 maps to 'C' def test_activity_init_with_unknown_player(self): """Test Activity with player not in player_map""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 9999, # Player not in map + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 9999, # Player not in map } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(player, '') + self.assertEqual(player, "") def test_activity_init_with_unknown_message_type(self): """Test Activity with unknown message type""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 999, # Unknown message type - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 999, # Unknown message type + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + # Unknown message type should result in no actions added self.assertEqual(len(activity.actions), 0) def test_activity_repr_with_actions(self): """Test Activity __repr__ with actions""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + repr_str = repr(activity) - self.assertIn('Activity', repr_str) - self.assertIn('Team One', repr_str) - self.assertIn('FA ADDED', repr_str) - self.assertIn('Player One', repr_str) + self.assertIn("Activity", repr_str) + self.assertIn("Team One", repr_str) + self.assertIn("FA ADDED", repr_str) + self.assertIn("Player One", repr_str) def test_activity_repr_empty_actions(self): """Test Activity __repr__ with no actions""" - data = { - 'date': '2023-01-01', - 'messages': [] - } - + data = {"date": "2023-01-01", "messages": []} + activity = Activity(data, self.player_map, self.get_team_data) - + repr_str = repr(activity) - self.assertEqual(repr_str, '') + self.assertEqual(repr_str, "") def test_activity_multiple_messages(self): """Test Activity with multiple messages""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 1001, }, { - 'messageTypeId': 179, - 'to': 'team2', - 'targetId': 1002, - } - ] + "messageTypeId": 179, + "to": "team2", + "targetId": 1002, + }, + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 2) - self.assertEqual(activity.actions[0][1], 'FA ADDED') - self.assertEqual(activity.actions[1][1], 'DROPPED') + self.assertEqual(activity.actions[0][1], "FA ADDED") + self.assertEqual(activity.actions[1][1], "DROPPED") def test_activity_with_position_map_invalid_position(self): """Test Activity with MOVED message with invalid position""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 188, - 'to': 999, # Position not in POSITION_MAP - 'targetId': 1001, + "messageTypeId": 188, + "to": 999, # Position not in POSITION_MAP + "targetId": 1001, } - ] + ], } - - activity = Activity(data, self.player_map, self.get_team_data, include_moved=True) - + + activity = Activity( + data, self.player_map, self.get_team_data, include_moved=True + ) + # Action should still be added but position will be empty self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(action, 'MOVED') - self.assertEqual(position, '') + self.assertEqual(action, "MOVED") + self.assertEqual(position, "") def test_activity_with_type_239_dropped(self): """Test Activity with type 239 (another DROPPED type)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 239, - 'for': 'team1', - 'targetId': 1001, + "messageTypeId": 239, + "for": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(action, 'DROPPED') + self.assertEqual(action, "DROPPED") def test_activity_with_type_181_dropped(self): """Test Activity with type 181 (another DROPPED type)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 181, - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 181, + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player, position = activity.actions[0] - self.assertEqual(action, 'DROPPED') + self.assertEqual(action, "DROPPED") diff --git a/tests/basketball/unit/test_league.py b/tests/basketball/unit/test_league.py index 0bf34ad34..be74b9135 100644 --- a/tests/basketball/unit/test_league.py +++ b/tests/basketball/unit/test_league.py @@ -5,148 +5,134 @@ class LeagueTest(TestCase): - + def test_league_init_no_fetch(self): """Test League initialization without fetching""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + # Verify that fetch_league was not called by checking that teams doesn't exist - self.assertFalse(hasattr(league, 'teams') or league.__dict__.get('teams') is not None) + self.assertFalse( + hasattr(league, "teams") or league.__dict__.get("teams") is not None + ) def test_league_map_matchup_ids_empty_schedule(self): """Test _map_matchup_ids with empty schedule""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + schedule = [] league._map_matchup_ids(schedule) - + self.assertEqual(league.matchup_ids, {}) def test_league_map_matchup_ids_single_matchup(self): """Test _map_matchup_ids with single matchup""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + schedule = [ { - 'matchupPeriodId': 1, - 'home': { - 'pointsByScoringPeriod': { - '1': 100.0, - '2': 95.0 - } - } + "matchupPeriodId": 1, + "home": {"pointsByScoringPeriod": {"1": 100.0, "2": 95.0}}, } ] - + league._map_matchup_ids(schedule) - - self.assertEqual(league.matchup_ids[1], ['1', '2']) + + self.assertEqual(league.matchup_ids[1], ["1", "2"]) def test_league_map_matchup_ids_multiple_periods(self): """Test _map_matchup_ids with multiple matchup periods""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + schedule = [ { - 'matchupPeriodId': 1, - 'home': { - 'pointsByScoringPeriod': { - '1': 100.0, - '2': 95.0 - } - } + "matchupPeriodId": 1, + "home": {"pointsByScoringPeriod": {"1": 100.0, "2": 95.0}}, }, { - 'matchupPeriodId': 2, - 'home': { - 'pointsByScoringPeriod': { - '3': 98.0, - '4': 92.0 - } - } - } + "matchupPeriodId": 2, + "home": {"pointsByScoringPeriod": {"3": 98.0, "4": 92.0}}, + }, ] - + league._map_matchup_ids(schedule) - - self.assertEqual(league.matchup_ids[1], ['1', '2']) - self.assertEqual(league.matchup_ids[2], ['3', '4']) + + self.assertEqual(league.matchup_ids[1], ["1", "2"]) + self.assertEqual(league.matchup_ids[2], ["3", "4"]) def test_league_map_matchup_ids_duplicate_periods(self): """Test _map_matchup_ids handles duplicate scoring periods""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + schedule = [ { - 'matchupPeriodId': 1, - 'home': { - 'pointsByScoringPeriod': { - '1': 100.0, - '2': 95.0 - } - } + "matchupPeriodId": 1, + "home": {"pointsByScoringPeriod": {"1": 100.0, "2": 95.0}}, }, { - 'matchupPeriodId': 1, - 'home': { - 'pointsByScoringPeriod': { - '2': 98.0, - '3': 92.0 - } - } - } + "matchupPeriodId": 1, + "home": {"pointsByScoringPeriod": {"2": 98.0, "3": 92.0}}, + }, ] - + league._map_matchup_ids(schedule) - + # Should merge and deduplicate - self.assertEqual(league.matchup_ids[1], ['1', '2', '3']) + self.assertEqual(league.matchup_ids[1], ["1", "2", "3"]) def test_league_map_matchup_ids_empty_scoring_periods(self): """Test _map_matchup_ids skips matchups with no scoring periods""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + schedule = [ - { - 'matchupPeriodId': 1, - 'home': { - 'pointsByScoringPeriod': {} # Empty - } - } + {"matchupPeriodId": 1, "home": {"pointsByScoringPeriod": {}}} # Empty ] - + league._map_matchup_ids(schedule) - + self.assertEqual(league.matchup_ids, {}) def test_league_standings_sorting(self): """Test standings are sorted by final_standing""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + # Create mock teams team1 = mock.MagicMock() team1.final_standing = 2 team1.standing = 5 - + team2 = mock.MagicMock() team2.final_standing = 1 team2.standing = 3 - + team3 = mock.MagicMock() team3.final_standing = 3 team3.standing = 4 - + league.teams = [team1, team2, team3] - + standings = league.standings() - + # Should be sorted by final_standing self.assertEqual(standings[0].final_standing, 1) self.assertEqual(standings[1].final_standing, 2) @@ -154,102 +140,122 @@ def test_league_standings_sorting(self): def test_league_standings_fallback_to_standing(self): """Test standings fallback to standing when final_standing is 0""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) - + # Create mock teams with zero final_standing team1 = mock.MagicMock() team1.final_standing = 0 team1.standing = 2 - + team2 = mock.MagicMock() team2.final_standing = 0 team2.standing = 1 - + league.teams = [team1, team2] - + standings = league.standings() - + # Should be sorted by standing when final_standing is 0 self.assertEqual(standings[0].standing, 1) self.assertEqual(standings[1].standing, 2) def test_league_recent_activity_year_check(self): """Test recent_activity raises exception for years before 2019""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2018, fetch_league=False) league.year = 2018 - + with self.assertRaises(Exception) as context: league.recent_activity() - - self.assertIn('Cant use recent activity before 2019', str(context.exception)) + + self.assertIn( + "Cant use recent activity before 2019", str(context.exception) + ) def test_league_transactions_valid_types(self): """Test transactions with valid types""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2023, fetch_league=False) league.scoringPeriodId = 1 league.espn_request = mock.MagicMock() - league.espn_request.league_get.return_value = {'transactions': []} + league.espn_request.league_get.return_value = {"transactions": []} league.player_map = {} - league.get_team_data = lambda x: '' - + league.get_team_data = lambda x: "" + # Valid types should not raise exception result = league.transactions(types={"WAIVER", "FREEAGENT"}) - + # Should call espn_request league.espn_request.league_get.assert_called_once() def test_league_free_agents_year_check(self): """Test free_agents raises exception for years before 2019""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2018, fetch_league=False) league.year = 2018 - + with self.assertRaises(Exception) as context: league.free_agents() - - self.assertIn('Cant use free agents before 2019', str(context.exception)) + + self.assertIn("Cant use free agents before 2019", str(context.exception)) def test_league_box_scores_year_check(self): """Test box_scores raises exception for years before 2019""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None): + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ): league = League(411647, 2018, fetch_league=False) league.year = 2018 - + with self.assertRaises(Exception) as context: league.box_scores() - - self.assertIn('Cant use box score before 2019', str(context.exception)) + + self.assertIn("Cant use box score before 2019", str(context.exception)) def test_league_init_with_espn_s2_swid(self): """Test League initialization with espn_s2 and swid""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None) as mock_init: - league = League(411647, 2023, espn_s2='test_s2', swid='test_swid', fetch_league=False) - + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ) as mock_init: + league = League( + 411647, 2023, espn_s2="test_s2", swid="test_swid", fetch_league=False + ) + mock_init.assert_called_once() call_kwargs = mock_init.call_args[1] - self.assertEqual(call_kwargs['league_id'], 411647) - self.assertEqual(call_kwargs['year'], 2023) - self.assertEqual(call_kwargs['sport'], 'nba') - self.assertEqual(call_kwargs['espn_s2'], 'test_s2') - self.assertEqual(call_kwargs['swid'], 'test_swid') - self.assertFalse(call_kwargs['debug']) + self.assertEqual(call_kwargs["league_id"], 411647) + self.assertEqual(call_kwargs["year"], 2023) + self.assertEqual(call_kwargs["sport"], "nba") + self.assertEqual(call_kwargs["espn_s2"], "test_s2") + self.assertEqual(call_kwargs["swid"], "test_swid") + self.assertFalse(call_kwargs["debug"]) def test_league_init_debug_mode(self): """Test League initialization with debug mode""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None) as mock_init: + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ) as mock_init: league = League(411647, 2023, debug=True, fetch_league=False) - + mock_init.assert_called_once() call_kwargs = mock_init.call_args[1] - self.assertTrue(call_kwargs['debug']) + self.assertTrue(call_kwargs["debug"]) def test_league_sport_is_nba(self): """Test that League always uses 'nba' as the sport""" - with mock.patch('espn_api.basketball.league.BaseLeague.__init__', return_value=None) as mock_init: + with mock.patch( + "espn_api.basketball.league.BaseLeague.__init__", return_value=None + ) as mock_init: league = League(411647, 2023, fetch_league=False) - + call_kwargs = mock_init.call_args[1] - self.assertEqual(call_kwargs['sport'], 'nba') + self.assertEqual(call_kwargs["sport"], "nba") diff --git a/tests/basketball/unit/test_player.py b/tests/basketball/unit/test_player.py index d2327a40a..2faf90f5b 100644 --- a/tests/basketball/unit/test_player.py +++ b/tests/basketball/unit/test_player.py @@ -4,60 +4,74 @@ from espn_api.basketball.constant import POSITION_MAP, PRO_TEAM_MAP -def _make_player_data(full_name='Test Player', player_id=1234, default_position_id=1, - lineup_slot_id=0, eligible_slots=None, pro_team_id=1, - acquisition_type='DRAFT', injury_status='ACTIVE', pos_rank=50, - expected_return_date=None, stats=None, player_extras=None): +def _make_player_data( + full_name="Test Player", + player_id=1234, + default_position_id=1, + lineup_slot_id=0, + eligible_slots=None, + pro_team_id=1, + acquisition_type="DRAFT", + injury_status="ACTIVE", + pos_rank=50, + expected_return_date=None, + stats=None, + player_extras=None, +): """Helper function to create player data""" if eligible_slots is None: eligible_slots = [lineup_slot_id] if stats is None: stats = [] - + return { - 'fullName': full_name, - 'id': player_id, - 'defaultPositionId': default_position_id, - 'lineupSlotId': lineup_slot_id, - 'eligibleSlots': eligible_slots, - 'acquisitionType': acquisition_type, - 'acquisitionDate': 1700000000000, - 'proTeamId': pro_team_id, - 'injuryStatus': injury_status, - 'positionalRanking': pos_rank, - 'expectedReturnDate': expected_return_date, - 'playerPoolEntry': { - 'player': { - 'fullName': full_name, - 'id': player_id, - 'injuryStatus': injury_status, - 'injured': False, - 'stats': stats, - **(player_extras or {}) + "fullName": full_name, + "id": player_id, + "defaultPositionId": default_position_id, + "lineupSlotId": lineup_slot_id, + "eligibleSlots": eligible_slots, + "acquisitionType": acquisition_type, + "acquisitionDate": 1700000000000, + "proTeamId": pro_team_id, + "injuryStatus": injury_status, + "positionalRanking": pos_rank, + "expectedReturnDate": expected_return_date, + "playerPoolEntry": { + "player": { + "fullName": full_name, + "id": player_id, + "injuryStatus": injury_status, + "injured": False, + "stats": stats, + **(player_extras or {}), } - } + }, } class PlayerTest(TestCase): - + def test_player_basic_init(self): """Test basic Player initialization""" - data = _make_player_data(full_name='LeBron James', player_id=1001, default_position_id=2) - + data = _make_player_data( + full_name="LeBron James", player_id=1001, default_position_id=2 + ) + player = Player(data, 2023) - - self.assertEqual(player.name, 'LeBron James') + + self.assertEqual(player.name, "LeBron James") self.assertEqual(player.playerId, 1001) self.assertEqual(player.year, 2023) - self.assertEqual(player.position, 'SG') # Position ID 2 (accounting for -1) = SG + self.assertEqual( + player.position, "SG" + ) # Position ID 2 (accounting for -1) = SG def test_player_position_mapping(self): """Test that player positions are correctly mapped""" # Test various positions positions = [1, 2, 3, 4, 5] - expected = ['PG', 'SG', 'SF', 'PF', 'C'] - + expected = ["PG", "SG", "SF", "PF", "C"] + for pos_id, expected_pos in zip(positions, expected): data = _make_player_data(default_position_id=pos_id) player = Player(data, 2023) @@ -66,8 +80,8 @@ def test_player_position_mapping(self): def test_player_pro_team_mapping(self): """Test that pro teams are correctly mapped""" # Test a few team IDs - pro_team_data = [(1, 'ATL'), (9, 'GSW'), (23, 'SAC'), (28, 'TOR')] - + pro_team_data = [(1, "ATL"), (9, "GSW"), (23, "SAC"), (28, "TOR")] + for team_id, expected_team in pro_team_data: data = _make_player_data(pro_team_id=team_id) player = Player(data, 2023) @@ -75,15 +89,15 @@ def test_player_pro_team_mapping(self): def test_player_acquisition_type(self): """Test player acquisition type""" - data = _make_player_data(acquisition_type='WAIVER') + data = _make_player_data(acquisition_type="WAIVER") player = Player(data, 2023) - self.assertEqual(player.acquisitionType, 'WAIVER') + self.assertEqual(player.acquisitionType, "WAIVER") def test_player_injury_status(self): """Test player injury status""" - data = _make_player_data(injury_status='OUT') + data = _make_player_data(injury_status="OUT") player = Player(data, 2023) - self.assertEqual(player.injuryStatus, 'OUT') + self.assertEqual(player.injuryStatus, "OUT") def test_player_positional_ranking(self): """Test player positional ranking""" @@ -96,13 +110,13 @@ def test_player_eligible_slots(self): data = _make_player_data(eligible_slots=[0, 1, 2]) player = Player(data, 2023) # eligible slots are mapped directly without -1 offset - self.assertEqual(player.eligibleSlots, ['PG', 'SG', 'SF']) + self.assertEqual(player.eligibleSlots, ["PG", "SG", "SF"]) def test_player_lineup_slot(self): """Test player lineup slot""" data = _make_player_data(lineup_slot_id=5) player = Player(data, 2023) - self.assertEqual(player.lineupSlot, 'G') # Position ID 5 = G + self.assertEqual(player.lineupSlot, "G") # Position ID 5 = G def test_player_expected_return_date(self): """Test player expected return date parsing""" @@ -156,88 +170,80 @@ def test_player_projected_points_zero(self): def test_player_repr(self): """Test player string representation""" - data = _make_player_data(full_name='Michael Jordan') + data = _make_player_data(full_name="Michael Jordan") player = Player(data, 2023) - self.assertEqual(repr(player), 'Player(Michael Jordan)') + self.assertEqual(repr(player), "Player(Michael Jordan)") def test_player_with_pro_schedule(self): """Test player schedule with pro schedule data""" pro_schedule = { - 1: { - '1': [ - { - 'awayProTeamId': 1, - 'homeProTeamId': 5, - 'date': 1700000000000 - } - ] - } + 1: {"1": [{"awayProTeamId": 1, "homeProTeamId": 5, "date": 1700000000000}]} } - + data = _make_player_data(pro_team_id=1) player = Player(data, 2023, pro_team_schedule=pro_schedule) - - self.assertIn('1', player.schedule) - self.assertEqual(player.schedule['1']['team'], 'CLE') # Home team ID 5 = CLE + + self.assertIn("1", player.schedule) + self.assertEqual(player.schedule["1"]["team"], "CLE") # Home team ID 5 = CLE def test_player_with_news(self): """Test player news parsing""" news_data = { - 'news': { - 'feed': [ + "news": { + "feed": [ { - 'published': '2023-01-15T10:00:00Z', - 'headline': 'Breaking News', - 'story': 'Player details here' + "published": "2023-01-15T10:00:00Z", + "headline": "Breaking News", + "story": "Player details here", } ] } } - + data = _make_player_data() player = Player(data, 2023, news=news_data) - + self.assertEqual(len(player.news), 1) - self.assertEqual(player.news[0]['headline'], 'Breaking News') + self.assertEqual(player.news[0]["headline"], "Breaking News") def test_player_with_stats_same_year(self): """Test player with stats for same year""" stats = [ { - 'seasonId': 2023, - 'id': '0010', - 'scoringPeriodId': 1, - 'appliedTotal': 50.0, - 'appliedAverage': 25.0, - 'stats': {'0': 100, '1': 5}, - 'averageStats': {'0': 50, '1': 2.5} + "seasonId": 2023, + "id": "0010", + "scoringPeriodId": 1, + "appliedTotal": 50.0, + "appliedAverage": 25.0, + "stats": {"0": 100, "1": 5}, + "averageStats": {"0": 50, "1": 2.5}, } ] - + data = _make_player_data(stats=stats) player = Player(data, 2023) - - self.assertIn('10_total', player.stats) - self.assertEqual(player.stats['10_total']['applied_total'], 50.0) + + self.assertIn("10_total", player.stats) + self.assertEqual(player.stats["10_total"]["applied_total"], 50.0) def test_player_with_stats_different_year(self): """Test player stats filtering by year""" stats = [ { - 'seasonId': 2022, # Different year - 'id': '0010', - 'scoringPeriodId': 1, - 'appliedTotal': 50.0, - 'appliedAverage': 25.0, - 'stats': {'0': 100, '1': 5} + "seasonId": 2022, # Different year + "id": "0010", + "scoringPeriodId": 1, + "appliedTotal": 50.0, + "appliedAverage": 25.0, + "stats": {"0": 100, "1": 5}, } ] - + data = _make_player_data(stats=stats) player = Player(data, 2023) - + # Stats from 2022 should not be included - self.assertNotIn('10_total', player.stats) + self.assertNotIn("10_total", player.stats) def test_player_nine_cat_averages_empty(self): """Test nine cat averages when no stats""" @@ -249,63 +255,63 @@ def test_player_stat_id_pretty_total(self): """Test stat ID pretty formatting for total""" data = _make_player_data() player = Player(data, 2023) - + # Test ID '0010' -> '10_total' - self.assertEqual(player._stat_id_pretty('0010', 1), '10_total') + self.assertEqual(player._stat_id_pretty("0010", 1), "10_total") def test_player_stat_id_pretty_projected(self): """Test stat ID pretty formatting for projected""" data = _make_player_data() player = Player(data, 2023) - + # Test ID '1010' -> '10_projected' - self.assertEqual(player._stat_id_pretty('1010', 1), '10_projected') + self.assertEqual(player._stat_id_pretty("1010", 1), "10_projected") def test_player_stat_id_pretty_fallback(self): """Test stat ID pretty formatting fallback""" data = _make_player_data() player = Player(data, 2023) - + # Test unknown ID format -> returns scoring period - self.assertEqual(player._stat_id_pretty('9910', 5), '5') + self.assertEqual(player._stat_id_pretty("9910", 5), "5") def test_player_injured_flag(self): """Test player injured flag""" - data = _make_player_data(player_extras={'injured': True}) + data = _make_player_data(player_extras={"injured": True}) player = Player(data, 2023) self.assertTrue(player.injured) def test_player_not_injured(self): """Test player not injured""" - data = _make_player_data(player_extras={'injured': False}) + data = _make_player_data(player_extras={"injured": False}) player = Player(data, 2023) self.assertFalse(player.injured) def test_player_with_pool_entry_nested(self): """Test player with playerPoolEntry nested data""" nested_data = { - 'fullName': 'Test Player', - 'id': 1234, - 'defaultPositionId': 1, - 'lineupSlotId': 0, - 'eligibleSlots': [0], - 'acquisitionType': 'DRAFT', - 'acquisitionDate': 1700000000000, - 'proTeamId': 1, - 'injuryStatus': 'ACTIVE', - 'positionalRanking': 50, - 'expectedReturnDate': None, - 'playerPoolEntry': { - 'player': { - 'fullName': 'Test Player', - 'id': 1234, - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'stats': [] + "fullName": "Test Player", + "id": 1234, + "defaultPositionId": 1, + "lineupSlotId": 0, + "eligibleSlots": [0], + "acquisitionType": "DRAFT", + "acquisitionDate": 1700000000000, + "proTeamId": 1, + "injuryStatus": "ACTIVE", + "positionalRanking": 50, + "expectedReturnDate": None, + "playerPoolEntry": { + "player": { + "fullName": "Test Player", + "id": 1234, + "injuryStatus": "ACTIVE", + "injured": False, + "stats": [], } - } + }, } - + player = Player(nested_data, 2023) - self.assertEqual(player.name, 'Test Player') + self.assertEqual(player.name, "Test Player") self.assertEqual(player.playerId, 1234) diff --git a/tests/basketball/unit/test_team.py b/tests/basketball/unit/test_team.py index 8c1ac6355..9f80ab993 100644 --- a/tests/basketball/unit/test_team.py +++ b/tests/basketball/unit/test_team.py @@ -3,95 +3,114 @@ from espn_api.basketball.player import Player -def _make_team_data(team_id=1, abbrev='LAL', name='Lakers', location='Los Angeles', nickname='Lakers', - division_id=1, wins=10, losses=5, ties=0, points_for=1000.0, points_against=950.0, - playoff_seed=1, rank_final=None, acquisitions=5, drops=3, trades=1, - acquisition_budget_spent=50, logo_url=None, stats_data=None): +def _make_team_data( + team_id=1, + abbrev="LAL", + name="Lakers", + location="Los Angeles", + nickname="Lakers", + division_id=1, + wins=10, + losses=5, + ties=0, + points_for=1000.0, + points_against=950.0, + playoff_seed=1, + rank_final=None, + acquisitions=5, + drops=3, + trades=1, + acquisition_budget_spent=50, + logo_url=None, + stats_data=None, +): """Helper function to create team data""" data = { - 'id': team_id, - 'abbrev': abbrev, - 'name': name, - 'location': location, - 'nickname': nickname, - 'divisionId': division_id, - 'record': { - 'overall': { - 'wins': wins, - 'losses': losses, - 'ties': ties, - 'pointsFor': points_for, - 'pointsAgainst': points_against + "id": team_id, + "abbrev": abbrev, + "name": name, + "location": location, + "nickname": nickname, + "divisionId": division_id, + "record": { + "overall": { + "wins": wins, + "losses": losses, + "ties": ties, + "pointsFor": points_for, + "pointsAgainst": points_against, } }, - 'playoffSeed': playoff_seed, - 'rankCalculatedFinal': rank_final, - 'transactionCounter': { - 'acquisitions': acquisitions, - 'acquisitionBudgetSpent': acquisition_budget_spent, - 'drops': drops, - 'trades': trades - } + "playoffSeed": playoff_seed, + "rankCalculatedFinal": rank_final, + "transactionCounter": { + "acquisitions": acquisitions, + "acquisitionBudgetSpent": acquisition_budget_spent, + "drops": drops, + "trades": trades, + }, } - + if logo_url: - data['logo'] = logo_url - + data["logo"] = logo_url + if stats_data: - data['valuesByStat'] = stats_data - + data["valuesByStat"] = stats_data + return data class TeamTest(TestCase): - + def setUp(self): """Set up test fixtures""" self.roster_data = { - 'entries': [ + "entries": [ { - 'fullName': 'Player One', - 'id': 1001, - 'defaultPositionId': 1, - 'lineupSlotId': 0, - 'eligibleSlots': [0], - 'acquisitionType': 'DRAFT', - 'acquisitionDate': 1700000000000, - 'proTeamId': 1, - 'injuryStatus': 'ACTIVE', - 'positionalRanking': 50, - 'expectedReturnDate': None, - 'playerPoolEntry': { - 'player': { - 'fullName': 'Player One', - 'id': 1001, - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'stats': [] + "fullName": "Player One", + "id": 1001, + "defaultPositionId": 1, + "lineupSlotId": 0, + "eligibleSlots": [0], + "acquisitionType": "DRAFT", + "acquisitionDate": 1700000000000, + "proTeamId": 1, + "injuryStatus": "ACTIVE", + "positionalRanking": 50, + "expectedReturnDate": None, + "playerPoolEntry": { + "player": { + "fullName": "Player One", + "id": 1001, + "injuryStatus": "ACTIVE", + "injured": False, + "stats": [], } - } + }, } ] } - + self.schedule_data = [] def test_team_basic_init(self): """Test basic team initialization""" - data = _make_team_data(team_id=1, name='Lakers', abbrev='LAL') - + data = _make_team_data(team_id=1, name="Lakers", abbrev="LAL") + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.team_id, 1) - self.assertEqual(team.team_name, 'Lakers') - self.assertEqual(team.team_abbrev, 'LAL') + self.assertEqual(team.team_name, "Lakers") + self.assertEqual(team.team_abbrev, "LAL") def test_team_record_stats(self): """Test team record statistics""" - data = _make_team_data(wins=20, losses=10, ties=0, points_for=2000.0, points_against=1800.0) - + data = _make_team_data( + wins=20, losses=10, ties=0, points_for=2000.0, points_against=1800.0 + ) + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.wins, 20) self.assertEqual(team.losses, 10) self.assertEqual(team.ties, 0) @@ -100,10 +119,12 @@ def test_team_record_stats(self): def test_team_transaction_stats(self): """Test team transaction statistics""" - data = _make_team_data(acquisitions=10, drops=5, trades=2, acquisition_budget_spent=100) - + data = _make_team_data( + acquisitions=10, drops=5, trades=2, acquisition_budget_spent=100 + ) + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.acquisitions, 10) self.assertEqual(team.drops, 5) self.assertEqual(team.trades, 2) @@ -112,270 +133,270 @@ def test_team_transaction_stats(self): def test_team_division_id(self): """Test team division ID""" data = _make_team_data(division_id=3) - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.division_id, 3) def test_team_playoff_seed(self): """Test team playoff seed""" data = _make_team_data(playoff_seed=5) - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.standing, 5) def test_team_final_standing(self): """Test team final standing""" data = _make_team_data(rank_final=3) - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.final_standing, 3) def test_team_division_name_default(self): """Test team division name defaults to empty""" data = _make_team_data() - + team = Team(data, self.roster_data, self.schedule_data, 2023) - - self.assertEqual(team.division_name, '') + + self.assertEqual(team.division_name, "") def test_team_logo_url(self): """Test team logo URL""" - logo = 'https://example.com/logo.png' + logo = "https://example.com/logo.png" data = _make_team_data(logo_url=logo) - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.logo_url, logo) def test_team_logo_url_default(self): """Test team logo URL defaults to empty""" data = _make_team_data() - + team = Team(data, self.roster_data, self.schedule_data, 2023) - - self.assertEqual(team.logo_url, '') + + self.assertEqual(team.logo_url, "") def test_team_stats_mapping(self): """Test team stats value mapping""" - stats_data = { - '0': 150.0, # PTS - '3': 20.0, # AST - '1': 10.0 # BLK - } + stats_data = {"0": 150.0, "3": 20.0, "1": 10.0} # PTS # AST # BLK data = _make_team_data(stats_data=stats_data) - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertIsNotNone(team.stats) - self.assertEqual(team.stats['PTS'], 150.0) - self.assertEqual(team.stats['AST'], 20.0) - self.assertEqual(team.stats['BLK'], 10.0) + self.assertEqual(team.stats["PTS"], 150.0) + self.assertEqual(team.stats["AST"], 20.0) + self.assertEqual(team.stats["BLK"], 10.0) def test_team_stats_none_by_default(self): """Test team stats is None when not provided""" data = _make_team_data() - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertIsNone(team.stats) def test_team_roster_initialization(self): """Test team roster initialization""" data = _make_team_data() - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(len(team.roster), 1) self.assertIsInstance(team.roster[0], Player) - self.assertEqual(team.roster[0].name, 'Player One') + self.assertEqual(team.roster[0].name, "Player One") def test_team_schedule_initialization(self): """Test team schedule initialization""" data = _make_team_data() - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(len(team.schedule), 0) def test_team_repr(self): """Test team string representation""" - data = _make_team_data(name='Lakers') - + data = _make_team_data(name="Lakers") + team = Team(data, self.roster_data, self.schedule_data, 2023) - - self.assertEqual(repr(team), 'Team(Lakers)') + + self.assertEqual(repr(team), "Team(Lakers)") def test_team_team_name_from_location_nickname(self): """Test team name constructed from location and nickname when name is not provided""" data = { - 'id': 1, - 'abbrev': 'LAL', - 'name': 'Unknown', # Triggers fallback - 'location': 'Los Angeles', - 'nickname': 'Lakers', - 'divisionId': 1, - 'record': { - 'overall': { - 'wins': 10, - 'losses': 5, - 'ties': 0, - 'pointsFor': 1000.0, - 'pointsAgainst': 950.0 + "id": 1, + "abbrev": "LAL", + "name": "Unknown", # Triggers fallback + "location": "Los Angeles", + "nickname": "Lakers", + "divisionId": 1, + "record": { + "overall": { + "wins": 10, + "losses": 5, + "ties": 0, + "pointsFor": 1000.0, + "pointsAgainst": 950.0, } }, - 'playoffSeed': 1, - 'rankCalculatedFinal': None, - 'transactionCounter': {} + "playoffSeed": 1, + "rankCalculatedFinal": None, + "transactionCounter": {}, } - + team = Team(data, self.roster_data, self.schedule_data, 2023) - - self.assertEqual(team.team_name, 'Los Angeles Lakers') + + self.assertEqual(team.team_name, "Los Angeles Lakers") def test_team_owners_kwarg(self): """Test team owners passed via kwargs""" data = _make_team_data() - owners = ['Owner One', 'Owner Two'] - + owners = ["Owner One", "Owner Two"] + team = Team(data, self.roster_data, self.schedule_data, 2023, owners=owners) - + self.assertEqual(team.owners, owners) def test_team_owners_default(self): """Test team owners defaults to empty list""" data = _make_team_data() - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.owners, []) def test_team_pro_schedule_kwarg(self): """Test pro schedule passed via kwargs""" data = _make_team_data() - pro_schedule = {1: {'1': [{'awayProTeamId': 1, 'homeProTeamId': 5, 'date': 1700000000000}]}} - - team = Team(data, self.roster_data, self.schedule_data, 2023, pro_schedule=pro_schedule) - + pro_schedule = { + 1: {"1": [{"awayProTeamId": 1, "homeProTeamId": 5, "date": 1700000000000}]} + } + + team = Team( + data, self.roster_data, self.schedule_data, 2023, pro_schedule=pro_schedule + ) + # Player should have schedule data self.assertGreater(len(team.roster[0].schedule), 0) def test_team_multiple_roster_players(self): """Test team with multiple roster players""" roster_data = { - 'entries': [ + "entries": [ { - 'fullName': 'Player One', - 'id': 1001, - 'defaultPositionId': 1, - 'lineupSlotId': 0, - 'eligibleSlots': [0], - 'acquisitionType': 'DRAFT', - 'acquisitionDate': 1700000000000, - 'proTeamId': 1, - 'injuryStatus': 'ACTIVE', - 'positionalRanking': 50, - 'expectedReturnDate': None, - 'playerPoolEntry': { - 'player': { - 'fullName': 'Player One', - 'id': 1001, - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'stats': [] + "fullName": "Player One", + "id": 1001, + "defaultPositionId": 1, + "lineupSlotId": 0, + "eligibleSlots": [0], + "acquisitionType": "DRAFT", + "acquisitionDate": 1700000000000, + "proTeamId": 1, + "injuryStatus": "ACTIVE", + "positionalRanking": 50, + "expectedReturnDate": None, + "playerPoolEntry": { + "player": { + "fullName": "Player One", + "id": 1001, + "injuryStatus": "ACTIVE", + "injured": False, + "stats": [], } - } + }, }, { - 'fullName': 'Player Two', - 'id': 1002, - 'defaultPositionId': 2, - 'lineupSlotId': 1, - 'eligibleSlots': [1], - 'acquisitionType': 'WAIVER', - 'acquisitionDate': 1700000000000, - 'proTeamId': 2, - 'injuryStatus': 'ACTIVE', - 'positionalRanking': 25, - 'expectedReturnDate': None, - 'playerPoolEntry': { - 'player': { - 'fullName': 'Player Two', - 'id': 1002, - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'stats': [] + "fullName": "Player Two", + "id": 1002, + "defaultPositionId": 2, + "lineupSlotId": 1, + "eligibleSlots": [1], + "acquisitionType": "WAIVER", + "acquisitionDate": 1700000000000, + "proTeamId": 2, + "injuryStatus": "ACTIVE", + "positionalRanking": 25, + "expectedReturnDate": None, + "playerPoolEntry": { + "player": { + "fullName": "Player Two", + "id": 1002, + "injuryStatus": "ACTIVE", + "injured": False, + "stats": [], } - } - } + }, + }, ] } data = _make_team_data() - + team = Team(data, roster_data, self.schedule_data, 2023) - + self.assertEqual(len(team.roster), 2) - self.assertEqual(team.roster[0].name, 'Player One') - self.assertEqual(team.roster[1].name, 'Player Two') + self.assertEqual(team.roster[0].name, "Player One") + self.assertEqual(team.roster[1].name, "Player Two") def test_team_points_against_rounding(self): """Test team points against is rounded to 2 decimals""" data = _make_team_data(points_against=1234.56789) - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.points_against, 1234.57) def test_team_rank_calculated_final_fallback(self): """Test rankCalculatedFinal used when rankFinal is None""" data = { - 'id': 1, - 'abbrev': 'LAL', - 'name': 'Lakers', - 'divisionId': 1, - 'record': { - 'overall': { - 'wins': 10, - 'losses': 5, - 'ties': 0, - 'pointsFor': 1000.0, - 'pointsAgainst': 950.0 + "id": 1, + "abbrev": "LAL", + "name": "Lakers", + "divisionId": 1, + "record": { + "overall": { + "wins": 10, + "losses": 5, + "ties": 0, + "pointsFor": 1000.0, + "pointsAgainst": 950.0, } }, - 'playoffSeed': 1, - 'rankFinal': None, - 'rankCalculatedFinal': 2, - 'transactionCounter': {} + "playoffSeed": 1, + "rankFinal": None, + "rankCalculatedFinal": 2, + "transactionCounter": {}, } - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.final_standing, 2) def test_team_default_transaction_counter(self): """Test default transaction counter when not provided""" data = { - 'id': 1, - 'abbrev': 'LAL', - 'name': 'Lakers', - 'divisionId': 1, - 'record': { - 'overall': { - 'wins': 10, - 'losses': 5, - 'ties': 0, - 'pointsFor': 1000.0, - 'pointsAgainst': 950.0 + "id": 1, + "abbrev": "LAL", + "name": "Lakers", + "divisionId": 1, + "record": { + "overall": { + "wins": 10, + "losses": 5, + "ties": 0, + "pointsFor": 1000.0, + "pointsAgainst": 950.0, } }, - 'playoffSeed': 1, - 'rankCalculatedFinal': None + "playoffSeed": 1, + "rankCalculatedFinal": None, } - + team = Team(data, self.roster_data, self.schedule_data, 2023) - + self.assertEqual(team.acquisitions, 0) self.assertEqual(team.drops, 0) self.assertEqual(team.trades, 0) diff --git a/tests/espn_requests/test_access_denied.py b/tests/espn_requests/test_access_denied.py index 349589a92..7c448d895 100644 --- a/tests/espn_requests/test_access_denied.py +++ b/tests/espn_requests/test_access_denied.py @@ -1,41 +1,67 @@ - from unittest import TestCase, mock from espn_api.requests.espn_requests import EspnFantasyRequests, ESPNAccessDenied + class DummyLogger: def log_request(self, **kwargs): pass + class TestAccessDenied(TestCase): def test_access_denied_no_cookies(self): - req = EspnFantasyRequests(sport='nfl', year=2024, league_id=123456, cookies=None, logger=DummyLogger()) + req = EspnFantasyRequests( + sport="nfl", year=2024, league_id=123456, cookies=None, logger=DummyLogger() + ) with self.assertRaises(ESPNAccessDenied) as excinfo: req.checkRequestStatus(401) - self.assertIn('espn_s2 and swid are required', str(excinfo.exception)) + self.assertIn("espn_s2 and swid are required", str(excinfo.exception)) def test_access_denied_missing_espn_s2(self): - cookies = {'SWID': 'some_swid'} - req = EspnFantasyRequests(sport='nfl', year=2024, league_id=123456, cookies=cookies, logger=DummyLogger()) + cookies = {"SWID": "some_swid"} + req = EspnFantasyRequests( + sport="nfl", + year=2024, + league_id=123456, + cookies=cookies, + logger=DummyLogger(), + ) with self.assertRaises(ESPNAccessDenied) as excinfo: req.checkRequestStatus(401) - self.assertIn('espn_s2 and swid are required', str(excinfo.exception)) + self.assertIn("espn_s2 and swid are required", str(excinfo.exception)) def test_access_denied_missing_swid(self): - cookies = {'espn_s2': 'some_s2'} - req = EspnFantasyRequests(sport='nfl', year=2024, league_id=123456, cookies=cookies, logger=DummyLogger()) + cookies = {"espn_s2": "some_s2"} + req = EspnFantasyRequests( + sport="nfl", + year=2024, + league_id=123456, + cookies=cookies, + logger=DummyLogger(), + ) with self.assertRaises(ESPNAccessDenied) as excinfo: req.checkRequestStatus(401) - self.assertIn('espn_s2 and swid are required', str(excinfo.exception)) + self.assertIn("espn_s2 and swid are required", str(excinfo.exception)) - @mock.patch('requests.get') + @mock.patch("requests.get") def test_access_denied_with_cookies(self, mock_get): - cookies = {'espn_s2': 'some_s2', 'SWID': 'some_swid'} - req = EspnFantasyRequests(sport='nfl', year=2024, league_id=123456, cookies=cookies, logger=DummyLogger()) + cookies = {"espn_s2": "some_s2", "SWID": "some_swid"} + req = EspnFantasyRequests( + sport="nfl", + year=2024, + league_id=123456, + cookies=cookies, + logger=DummyLogger(), + ) + class DummyResponse: status_code = 401 + def json(self): return {} + mock_get.return_value = DummyResponse() with self.assertRaises(ESPNAccessDenied) as excinfo: req.checkRequestStatus(401) - self.assertIn(f"League {req.league_id} cannot be accessed", str(excinfo.exception)) + self.assertIn( + f"League {req.league_id} cannot be accessed", str(excinfo.exception) + ) diff --git a/tests/espn_requests/test_espn_requests.py b/tests/espn_requests/test_espn_requests.py index a73eb8c82..c9c6d5909 100644 --- a/tests/espn_requests/test_espn_requests.py +++ b/tests/espn_requests/test_espn_requests.py @@ -3,12 +3,13 @@ import io from espn_api.requests.espn_requests import EspnFantasyRequests + class EspnRequestsTest(TestCase): @requests_mock.Mocker() - @mock.patch('sys.stdout', new_callable=io.StringIO) + @mock.patch("sys.stdout", new_callable=io.StringIO) def test_stub(self, mock_request, mock_stdout): - url_api_key = 'https://registerdisney.go.com/jgc/v5/client/ESPN-FANTASYLM-PROD/api-key?langPref=en-US' + url_api_key = "https://registerdisney.go.com/jgc/v5/client/ESPN-FANTASYLM-PROD/api-key?langPref=en-US" mock_request.post(url_api_key, status_code=400) # @requests_mock.Mocker() @@ -19,7 +20,7 @@ def test_stub(self, mock_request, mock_stdout): # request = EspnFantasyRequests(sport='nfl', league_id=1234, year=2019) # request.authentication(username='user', password='pass') # self.assertEqual(mock_stdout.getvalue(), 'Unable to access API-Key\nRetry the authentication or continuing without private league access\n') - + # @requests_mock.Mocker() # @mock.patch('sys.stdout', new_callable=io.StringIO) # def test_authentication_login_fail(self, mock_request, mock_stdout): @@ -31,7 +32,7 @@ def test_stub(self, mock_request, mock_stdout): # request = EspnFantasyRequests(sport='nfl', league_id=1234, year=2019) # request.authentication(username='user', password='pass') # self.assertEqual(mock_stdout.getvalue(), 'Authentication unsuccessful - check username and password input\nRetry the authentication or continuing without private league access\n') - + # @requests_mock.Mocker() # @mock.patch('sys.stdout', new_callable=io.StringIO) # def test_authentication_login_error(self, mock_request, mock_stdout): @@ -43,7 +44,7 @@ def test_stub(self, mock_request, mock_stdout): # request = EspnFantasyRequests(sport='nfl', league_id=1234, year=2019) # request.authentication(username='user', password='pass') # self.assertEqual(mock_stdout.getvalue(), 'Authentication unsuccessful - error:{}\nRetry the authentication or continuing without private league access\n') - + # @requests_mock.Mocker() # def test_authentication_pass(self, mock_request): # url_api_key = 'https://registerdisney.go.com/jgc/v5/client/ESPN-FANTASYLM-PROD/api-key?langPref=en-US' @@ -54,4 +55,4 @@ def test_stub(self, mock_request, mock_stdout): # request = EspnFantasyRequests(sport='nfl', league_id=1234, year=2019) # request.authentication(username='user', password='pass') # self.assertEqual(request.cookies['espn_s2'], 'cookie1') - # self.assertEqual(request.cookies['swid'], 'cookie2') \ No newline at end of file + # self.assertEqual(request.cookies['swid'], 'cookie2') diff --git a/tests/football/integration/test_league.py b/tests/football/integration/test_league.py index ef1fe5ccf..517762f76 100644 --- a/tests/football/integration/test_league.py +++ b/tests/football/integration/test_league.py @@ -1,6 +1,7 @@ from unittest import TestCase, skip from espn_api.football import League + # Integration test to make sure ESPN's API didnt change class LeagueTest(TestCase): @@ -8,16 +9,22 @@ def test_league_init(self): league = League(1234, 2018) self.assertEqual(league.current_week, 17) - @skip('Need new league id for test') + + @skip("Need new league id for test") def test_past_league(self): league = League(368876, 2017) self.assertEqual(league.nfl_week, 18) def test_private_league(self): - ''' Test for switching to fallback API endpoint for private leagues. Random, incorrect cookies used to force fallback. ''' + """Test for switching to fallback API endpoint for private leagues. Random, incorrect cookies used to force fallback.""" with self.assertRaises(Exception): - League(368876, 2018, 'AEF1234567890ABCDE1234567890ABCD', '{D0C25A4C-2A0D-4E56-8E7F-20A10B663272}') + League( + 368876, + 2018, + "AEF1234567890ABCDE1234567890ABCD", + "{D0C25A4C-2A0D-4E56-8E7F-20A10B663272}", + ) def test_unknown_league(self): with self.assertRaises(Exception): @@ -40,25 +47,28 @@ def test_box_scores(self): box_scores = league.box_scores(week=2) - self.assertEqual(repr(box_scores[1].away_team), 'Team(TEAM BERRY)') - self.assertEqual(repr(box_scores[1].away_lineup[1]), 'Player(Odell Beckham Jr., points:29.0, projected:16.72)') - self.assertEqual(repr(box_scores[1]), 'Box Score(Team(TEAM BERRY) at Team(TEAM HOLLAND))') + self.assertEqual(repr(box_scores[1].away_team), "Team(TEAM BERRY)") + self.assertEqual( + repr(box_scores[1].away_lineup[1]), + "Player(Odell Beckham Jr., points:29.0, projected:16.72)", + ) + self.assertEqual( + repr(box_scores[1]), "Box Score(Team(TEAM BERRY) at Team(TEAM HOLLAND))" + ) self.assertEqual(box_scores[0].is_playoff, False) player = box_scores[1].away_lineup[1] - self.assertTrue(hasattr(player, 'breakdown')) - self.assertTrue(hasattr(player, 'points_breakdown')) + self.assertTrue(hasattr(player, "breakdown")) + self.assertTrue(hasattr(player, "points_breakdown")) self.assertNotEqual(player.breakdown, {}) self.assertNotEqual(player.points_breakdown, {}) - self.assertEqual(player.breakdown['receivingTouchdowns'], 1.0) - self.assertEqual(player.points_breakdown['receivingTouchdowns'], 6.0) - self.assertEqual(player.projected_breakdown['receivingTouchdowns'], 0.637185906) - self.assertEqual(player.projected_points_breakdown['receivingTouchdowns'], 3.823115436) - - - - + self.assertEqual(player.breakdown["receivingTouchdowns"], 1.0) + self.assertEqual(player.points_breakdown["receivingTouchdowns"], 6.0) + self.assertEqual(player.projected_breakdown["receivingTouchdowns"], 0.637185906) + self.assertEqual( + player.projected_points_breakdown["receivingTouchdowns"], 3.823115436 + ) box_scores = league.box_scores() self.assertEqual(box_scores[0].is_playoff, True) @@ -68,13 +78,13 @@ def test_player_info(self): # Single ID player = league.player_info(playerId=3139477) - self.assertEqual(player.name, 'Patrick Mahomes') + self.assertEqual(player.name, "Patrick Mahomes") # Two ID players = league.player_info(playerId=[3139477, 3068267]) self.assertEqual(len(players), 2) - self.assertEqual(players[0].name, 'Patrick Mahomes') - self.assertEqual(players[1].name, 'Austin Ekeler') + self.assertEqual(players[0].name, "Patrick Mahomes") + self.assertEqual(players[1].name, "Austin Ekeler") def test_blank_league_init(self): blank_league = League(48153503, 2019, fetch_league=False) diff --git a/tests/football/unit/test_league.py b/tests/football/unit/test_league.py index 77406e850..7686be488 100644 --- a/tests/football/unit/test_league.py +++ b/tests/football/unit/test_league.py @@ -20,75 +20,103 @@ class LeagueTest(TestCase): def setUp(self): self.league_id = 123 self.season = 2018 - self.espn_endpoint = FANTASY_BASE_ENDPOINT + 'FFL/seasons/' + str(self.season) + '/segments/0/leagues/' + str(self.league_id) - self.players_endpoint = FANTASY_BASE_ENDPOINT + 'ffl/seasons/' + str(self.season) + '/players?view=players_wl' - self.base_endpoint = FANTASY_BASE_ENDPOINT + 'ffl/seasons/' + str(self.season) - with open('tests/football/unit/data/league_2018_data.json') as data: + self.espn_endpoint = ( + FANTASY_BASE_ENDPOINT + + "FFL/seasons/" + + str(self.season) + + "/segments/0/leagues/" + + str(self.league_id) + ) + self.players_endpoint = ( + FANTASY_BASE_ENDPOINT + + "ffl/seasons/" + + str(self.season) + + "/players?view=players_wl" + ) + self.base_endpoint = FANTASY_BASE_ENDPOINT + "ffl/seasons/" + str(self.season) + with open("tests/football/unit/data/league_2018_data.json") as data: self.league_data = json.loads(data.read()) - with open('tests/football/unit/data/league_draft_2018.json') as data: + with open("tests/football/unit/data/league_draft_2018.json") as data: self.draft_data = json.loads(data.read()) - with open('tests/football/unit/data/league_players_2018.json') as data: + with open("tests/football/unit/data/league_players_2018.json") as data: self.players_data = json.loads(data.read()) - with open('tests/football/unit/data/league_2019_playerCard.json') as data: + with open("tests/football/unit/data/league_2019_playerCard.json") as data: self.player_card_data = json.loads(data.read()) - with open('tests/football/unit/data/pro_schedule_2024.json') as data: + with open("tests/football/unit/data/pro_schedule_2024.json") as data: self.pro_schedule_data = json.loads(data.read()) - + def mock_setUp(self, m): - m.get(self.espn_endpoint + '?view=mTeam&view=mRoster&view=mMatchup&view=mSettings', status_code=200, json=self.league_data) - m.get(self.espn_endpoint + '?view=mDraftDetail', status_code=200, json=self.draft_data) + m.get( + self.espn_endpoint + + "?view=mTeam&view=mRoster&view=mMatchup&view=mSettings", + status_code=200, + json=self.league_data, + ) + m.get( + self.espn_endpoint + "?view=mDraftDetail", + status_code=200, + json=self.draft_data, + ) m.get(self.players_endpoint, status_code=200, json=self.players_data) - m.get(self.base_endpoint + '?view=proTeamSchedules_wl', status_code=200, json=self.pro_schedule_data) + m.get( + self.base_endpoint + "?view=proTeamSchedules_wl", + status_code=200, + json=self.pro_schedule_data, + ) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_error_status(self, m): m.get(self.espn_endpoint, status_code=501, json=self.league_data) with self.assertRaises(Exception): League(self.league_id, self.season) - - @requests_mock.Mocker() + + @requests_mock.Mocker() def test_unknown_error_status(self, m): m.get(self.espn_endpoint, status_code=300, json=self.league_data) with self.assertRaises(Exception): League(self.league_id, self.season) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_create_object(self, m): self.mock_setUp(m) league = League(self.league_id, self.season) - self.assertEqual(repr(league), 'League(123, 2018)') - self.assertEqual(repr(league.settings), 'Settings(FXBG League)') - self.assertEqual(league.settings.scoring_format[0]['abbr'], 'BLKKRTD') + self.assertEqual(repr(league), "League(123, 2018)") + self.assertEqual(repr(league.settings), "Settings(FXBG League)") + self.assertEqual(league.settings.scoring_format[0]["abbr"], "BLKKRTD") self.assertEqual(league.current_week, 16) self.assertEqual(len(league.teams), 10) league.refresh() - self.assertEqual(repr(league), 'League(123, 2018)') - self.assertEqual(repr(league.settings), 'Settings(FXBG League)') + self.assertEqual(repr(league), "League(123, 2018)") + self.assertEqual(repr(league.settings), "Settings(FXBG League)") self.assertEqual(league.current_week, 16) self.assertEqual(len(league.teams), 10) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_load_roster_week(self, m): self.mock_setUp(m) league = League(self.league_id, self.season) - - with open('tests/football/unit/data/league_roster_week1.json') as f: + + with open("tests/football/unit/data/league_roster_week1.json") as f: data = json.loads(f.read()) - m.get(self.espn_endpoint + '?view=mRoster&scoringPeriodId=1', status_code=200, json=data) + m.get( + self.espn_endpoint + "?view=mRoster&scoringPeriodId=1", + status_code=200, + json=data, + ) league.load_roster_week(1) # check player that I know is on roster - name = '' + name = "" team = league.teams[1] for player in team.roster: if player.name == "Le'Veon Bell": name = player.name self.assertEqual(name, "Le'Veon Bell") - - @requests_mock.Mocker() + + @requests_mock.Mocker() def test_league_standings(self, m): self.mock_setUp(m) @@ -322,8 +350,8 @@ def test_top_scorer(self, m): team = league.top_scorer() self.assertEqual(team.team_id, 1) - - @requests_mock.Mocker() + + @requests_mock.Mocker() def test_least_scorer(self, m): self.mock_setUp(m) @@ -332,7 +360,7 @@ def test_least_scorer(self, m): team = league.least_scorer() self.assertEqual(team.team_id, 10) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_most_pa(self, m): self.mock_setUp(m) @@ -341,14 +369,14 @@ def test_most_pa(self, m): team = league.most_points_against() self.assertEqual(team.team_id, 2) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_top_scored(self, m): self.mock_setUp(m) league = League(self.league_id, self.season) team = league.top_scored_week() - self.assertEqual(team[0].team_id, 5) + self.assertEqual(team[0].team_id, 5) @requests_mock.Mocker() def test_least_scored(self, m): @@ -358,7 +386,7 @@ def test_least_scored(self, m): team = league.least_scored_week() self.assertEqual(team[0].team_id, 10) - + @requests_mock.Mocker() def test_get_team(self, m): self.mock_setUp(m) @@ -366,29 +394,35 @@ def test_get_team(self, m): league = League(self.league_id, self.season) team = league.get_team_data(8) - self.assertEqual(team.team_id, 8) + self.assertEqual(team.team_id, 8) team = league.get_team_data(18) self.assertEqual(team, None) - - @requests_mock.Mocker() + + @requests_mock.Mocker() def test_get_scoreboard(self, m): self.mock_setUp(m) league = League(self.league_id, self.season) - - with open('tests/football/unit/data/league_matchupScore_2018.json') as f: + + with open("tests/football/unit/data/league_matchupScore_2018.json") as f: data = json.loads(f.read()) - m.get(self.espn_endpoint + '?view=mMatchupScore', status_code=200, json=data) + m.get(self.espn_endpoint + "?view=mMatchupScore", status_code=200, json=data) scoreboard = league.scoreboard(1) - self.assertEqual(repr(scoreboard[1]), 'Matchup(Team(Watch What You Saquon), Team(Feel the Brees))') + self.assertEqual( + repr(scoreboard[1]), + "Matchup(Team(Watch What You Saquon), Team(Feel the Brees))", + ) self.assertEqual(scoreboard[0].home_score, 125.5) scoreboard = league.scoreboard() - self.assertEqual(repr(scoreboard[-1]), 'Matchup(Team(Jacking Goff On Sundays), Team(Feel the Brees))') + self.assertEqual( + repr(scoreboard[-1]), + "Matchup(Team(Jacking Goff On Sundays), Team(Feel the Brees))", + ) self.assertEqual(scoreboard[-1].away_score, 108.64) - + @requests_mock.Mocker() def test_player(self, m): self.mock_setUp(m) @@ -396,11 +430,11 @@ def test_player(self, m): league = League(self.league_id, self.season) team = league.teams[2] - self.assertEqual(repr(team.roster[0]), 'Player(Drew Brees)') - self.assertEqual(team.roster[0].schedule['1']['team'], 'CAR') - self.assertEqual(team.get_player_name(2521161), 'Zach Zenner') - self.assertEqual(team.get_player_name(0), '') - + self.assertEqual(repr(team.roster[0]), "Player(Drew Brees)") + self.assertEqual(team.roster[0].schedule["1"]["team"], "CAR") + self.assertEqual(team.get_player_name(2521161), "Zach Zenner") + self.assertEqual(team.get_player_name(0), "") + @requests_mock.Mocker() def test_draft(self, m): self.mock_setUp(m) @@ -409,18 +443,23 @@ def test_draft(self, m): first_pick = league.draft[0] third_pick = league.draft[2] - self.assertEqual(repr(first_pick), 'Pick(R:1 P:1, Le\'Veon Bell, Team(Rollin\' With Mahomies))') + self.assertEqual( + repr(first_pick), "Pick(R:1 P:1, Le'Veon Bell, Team(Rollin' With Mahomies))" + ) self.assertEqual(third_pick.round_num, 1) self.assertEqual(third_pick.round_pick, 3) - self.assertEqual(third_pick.auction_repr(), 'Team(Goin\' HAM Newton), 13934, Antonio Brown, 0, False') + self.assertEqual( + third_pick.auction_repr(), + "Team(Goin' HAM Newton), 13934, Antonio Brown, 0, False", + ) # TODO need to get data for most recent season - # @requests_mock.Mocker() + # @requests_mock.Mocker() # def test_box_score(self, m): # self.mock_setUp(m) # league = League(self.league_id, self.season) - + # with open('tests/unit/data/league_boxscore_2018.json') as f: # data = json.loads(f.read()) # m.get(self.espn_endpoint + '?view=mMatchup&view=mMatchupScore&scoringPeriodId=13', status_code=200, json=data) @@ -428,7 +467,7 @@ def test_draft(self, m): # self.assertEqual(repr(box_scores[0].home_team), 'Team(Rollin\' With Mahomies)') # self.assertEqual(repr(box_scores[0].home_lineup[1]), 'Player(Christian McCaffrey, points:31, projected:23)') - + @requests_mock.Mocker() def test_power_rankings(self, m): self.mock_setUp(m) @@ -443,65 +482,84 @@ def test_power_rankings(self, m): self.assertEqual(empty_week, current_week) valid_week = league.power_rankings(13) - self.assertEqual(valid_week[0][0], '71.15') - self.assertEqual(repr(valid_week[0][1]), 'Team(Perscription Mixon)') + self.assertEqual(valid_week[0][0], "71.15") + self.assertEqual(repr(valid_week[0][1]), "Team(Perscription Mixon)") @requests_mock.Mocker() - @mock.patch.object(League, '_get_pro_schedule') - @mock.patch.object(League, '_get_positional_ratings') - @mock.patch.object(BoxPlayer, '__init__') + @mock.patch.object(League, "_get_pro_schedule") + @mock.patch.object(League, "_get_positional_ratings") + @mock.patch.object(BoxPlayer, "__init__") def test_free_agents(self, m, mock_boxplayer, mock_nfl_schedule, mock_pos_ratings): self.mock_setUp(m) mock_boxplayer.return_value = None league = League(self.league_id, self.season) - m.get(self.espn_endpoint + '?view=kona_player_info&scoringPeriodId=16', status_code=200, json={'players': [1, 2]}) + m.get( + self.espn_endpoint + "?view=kona_player_info&scoringPeriodId=16", + status_code=200, + json={"players": [1, 2]}, + ) league.year = 2019 - free_agents = league.free_agents(position='QB', position_id=0) + free_agents = league.free_agents(position="QB", position_id=0) self.assertEqual(len(free_agents), 2) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_recent_activity(self, m): self.mock_setUp(m) league = League(self.league_id, 2018) - + # TODO hack until I get all mock data for 2019 - league.year = 2019 - self.espn_endpoint = FANTASY_BASE_ENDPOINT + 'ffl/seasons/' + str(2019) + '/segments/0/leagues/' + str(self.league_id) + league.year = 2019 + self.espn_endpoint = ( + FANTASY_BASE_ENDPOINT + + "ffl/seasons/" + + str(2019) + + "/segments/0/leagues/" + + str(self.league_id) + ) league.espn_request.LEAGUE_ENDPOINT = self.espn_endpoint - with open('tests/football/unit/data/league_recent_activity_2019.json') as f: + with open("tests/football/unit/data/league_recent_activity_2019.json") as f: data = json.loads(f.read()) - m.get(self.espn_endpoint + '/communication/?view=kona_league_communication', status_code=200, json=data) - m.get(self.espn_endpoint + '?view=kona_playercard', status_code=200, json=self.player_card_data) + m.get( + self.espn_endpoint + "/communication/?view=kona_league_communication", + status_code=200, + json=data, + ) + m.get( + self.espn_endpoint + "?view=kona_playercard", + status_code=200, + json=self.player_card_data, + ) - activity = league.recent_activity() - self.assertEqual(repr(activity[0].actions[0][0]), 'Team(Perscription Mixon)') + activity = league.recent_activity() + self.assertEqual(repr(activity[0].actions[0][0]), "Team(Perscription Mixon)") self.assertEqual(len(repr(activity)), 2829) - @mock.patch.object(League, '_fetch_league') + @mock.patch.object(League, "_fetch_league") def test_cookie_set(self, mock_fetch_league): - league = League(league_id=1234, year=2019, espn_s2='cookie1', swid='cookie2') - self.assertEqual(league.espn_request.cookies['espn_s2'], 'cookie1') - self.assertEqual(league.espn_request.cookies['SWID'], 'cookie2') - + league = League(league_id=1234, year=2019, espn_s2="cookie1", swid="cookie2") + self.assertEqual(league.espn_request.cookies["espn_s2"], "cookie1") + self.assertEqual(league.espn_request.cookies["SWID"], "cookie2") + @requests_mock.Mocker() def test_player_info(self, m): self.mock_setUp(m) - m.get(self.espn_endpoint + '?view=kona_playercard', status_code=200, json=self.player_card_data) + m.get( + self.espn_endpoint + "?view=kona_playercard", + status_code=200, + json=self.player_card_data, + ) league = League(self.league_id, self.season) league.year = 2019 # Invalid name - player = league.player_info('Test 1') + player = league.player_info("Test 1") self.assertEqual(player, None) - player = league.player_info('James Conner') - self.assertEqual(player.name, 'James Conner') - self.assertEqual(player.stats[1]['points'], 10.5) + player = league.player_info("James Conner") + self.assertEqual(player.name, "James Conner") + self.assertEqual(player.stats[1]["points"], 10.5) self.assertEqual(player.percent_owned, 96.73) self.assertEqual(player.percent_started, 73.87) - - - diff --git a/tests/football/unit/test_past_league.py b/tests/football/unit/test_past_league.py index f27a7bb98..e91bc3c49 100644 --- a/tests/football/unit/test_past_league.py +++ b/tests/football/unit/test_past_league.py @@ -5,30 +5,52 @@ import json - class LeaguePastTest(TestCase): def setUp(self): self.league_id = 123 self.season = 2015 - self.espn_endpoint = FANTASY_BASE_ENDPOINT + 'ffl/leagueHistory/' + str(self.league_id) + '?seasonId=2015' - self.players_endpoint = FANTASY_BASE_ENDPOINT + 'ffl/seasons/' + str(self.season) + '/players?view=players_wl' - self.base_endpoint = FANTASY_BASE_ENDPOINT + 'ffl/seasons/' + str(self.season) - with open('tests/football/unit/data/league_2015_data.json') as data: + self.espn_endpoint = ( + FANTASY_BASE_ENDPOINT + + "ffl/leagueHistory/" + + str(self.league_id) + + "?seasonId=2015" + ) + self.players_endpoint = ( + FANTASY_BASE_ENDPOINT + + "ffl/seasons/" + + str(self.season) + + "/players?view=players_wl" + ) + self.base_endpoint = FANTASY_BASE_ENDPOINT + "ffl/seasons/" + str(self.season) + with open("tests/football/unit/data/league_2015_data.json") as data: self.league_data = json.loads(data.read()) - with open('tests/football/unit/data/league_draft_2015.json') as data: + with open("tests/football/unit/data/league_draft_2015.json") as data: self.draft_data = json.loads(data.read()) - with open('tests/football/unit/data/league_players_2015.json') as data: + with open("tests/football/unit/data/league_players_2015.json") as data: self.players_data = json.loads(data.read()) - with open('tests/football/unit/data/pro_schedule_2024.json') as data: + with open("tests/football/unit/data/pro_schedule_2024.json") as data: self.pro_schedule_data = json.loads(data.read()) - + def mock_setUp(self, m): - m.get(self.espn_endpoint + '&view=mTeam&view=mRoster&view=mMatchup&view=mSettings', status_code=200, json=self.league_data) - m.get(self.espn_endpoint + '&view=mDraftDetail', status_code=200, json=self.draft_data) + m.get( + self.espn_endpoint + + "&view=mTeam&view=mRoster&view=mMatchup&view=mSettings", + status_code=200, + json=self.league_data, + ) + m.get( + self.espn_endpoint + "&view=mDraftDetail", + status_code=200, + json=self.draft_data, + ) m.get(self.players_endpoint, status_code=200, json=self.players_data) - m.get(self.base_endpoint + '?view=proTeamSchedules_wl', status_code=200, json=self.pro_schedule_data) + m.get( + self.base_endpoint + "?view=proTeamSchedules_wl", + status_code=200, + json=self.pro_schedule_data, + ) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_create_object(self, m): self.mock_setUp(m) league = League(self.league_id, self.season) @@ -36,24 +58,28 @@ def test_create_object(self, m): self.assertEqual(league.nfl_week, 18) self.assertEqual(len(league.teams), 8) - @requests_mock.Mocker() + @requests_mock.Mocker() def test_get_scoreboard(self, m): self.mock_setUp(m) league = League(self.league_id, self.season) - - with open('tests/football/unit/data/league_matchupScore_2015.json') as f: + + with open("tests/football/unit/data/league_matchupScore_2015.json") as f: data = json.loads(f.read()) - m.get(self.espn_endpoint + '&view=mMatchupScore', status_code=200, json=data) + m.get(self.espn_endpoint + "&view=mMatchupScore", status_code=200, json=data) scoreboard = league.scoreboard(1) - self.assertEqual(repr(scoreboard[1]), 'Matchup(Team(Go Deep Jack ), Team(Last Place))') + self.assertEqual( + repr(scoreboard[1]), "Matchup(Team(Go Deep Jack ), Team(Last Place))" + ) self.assertEqual(scoreboard[0].home_score, 133) scoreboard = league.scoreboard() - self.assertEqual(repr(scoreboard[-1]), 'Matchup(Team(Go Deep Jack ), Team(Last Place))') + self.assertEqual( + repr(scoreboard[-1]), "Matchup(Team(Go Deep Jack ), Team(Last Place))" + ) self.assertEqual(scoreboard[-1].away_score, 123) - + @requests_mock.Mocker() def test_draft(self, m): self.mock_setUp(m) @@ -62,10 +88,12 @@ def test_draft(self, m): first_pick = league.draft[0] third_pick = league.draft[2] - self.assertEqual(repr(first_pick), 'Pick(R:1 P:1, Eddie Lacy, Team(Show Me Your TD\'s))') + self.assertEqual( + repr(first_pick), "Pick(R:1 P:1, Eddie Lacy, Team(Show Me Your TD's))" + ) self.assertEqual(third_pick.round_num, 1) self.assertEqual(third_pick.round_pick, 3) - + @requests_mock.Mocker() def test_box_score_fails(self, m): self.mock_setUp(m) @@ -74,7 +102,7 @@ def test_box_score_fails(self, m): with self.assertRaises(Exception): league.box_scores(1) - + @requests_mock.Mocker() def test_free_agents_fails(self, m): self.mock_setUp(m) diff --git a/tests/hockey/integration/test_league.py b/tests/hockey/integration/test_league.py index 911369658..33d11ad49 100644 --- a/tests/hockey/integration/test_league.py +++ b/tests/hockey/integration/test_league.py @@ -8,9 +8,9 @@ class LeagueTest(TestCase): def test_league_init(self): league = League(77421173, 2021) - self.assertEqual(league.teams[0].__repr__(), 'Team(Cambridge Bay Caribou)') - self.assertEqual(league.teams[1].roster[0].name, 'Steven Stamkos') + self.assertEqual(league.teams[0].__repr__(), "Team(Cambridge Bay Caribou)") + self.assertEqual(league.teams[1].roster[0].name, "Steven Stamkos") def test_blank_league_init(self): blank_league = League(77421173, 2021, fetch_league=False) - self.assertEqual(len(blank_league.teams), 0) \ No newline at end of file + self.assertEqual(len(blank_league.teams), 0) diff --git a/tests/hockey/unit/test_league.py b/tests/hockey/unit/test_league.py index 5677d3bed..c559ee17d 100644 --- a/tests/hockey/unit/test_league.py +++ b/tests/hockey/unit/test_league.py @@ -10,11 +10,10 @@ class BaseLeagueTest(TestCase): def setUp(self) -> None: self.league_id = 1 self.season = 2020 - self.league = BaseLeague(self.league_id, self.season, sport= 'nhl') - - with open('tests/hockey/unit/data/league_data.json') as data: - self.league_data = json.loads(data.read()) + self.league = BaseLeague(self.league_id, self.season, sport="nhl") + with open("tests/hockey/unit/data/league_data.json") as data: + self.league_data = json.loads(data.read()) def test_base_league(self): self.assertEqual(self.league.league_id, 1) @@ -23,7 +22,7 @@ def test_base_league(self): self.assertEqual(self.league.draft, []) self.assertEqual(self.league.player_map, {}) - @mock.patch.object(EspnFantasyRequests, 'get_league') + @mock.patch.object(EspnFantasyRequests, "get_league") def test_base_league_fetch_league(self, mock_get_league_request): mock_get_league_request.return_value = self.league_data @@ -32,21 +31,21 @@ def test_base_league_fetch_league(self, mock_get_league_request): self.assertIsNotNone(self.league.currentMatchupPeriod) - @mock.patch.object(EspnFantasyRequests, 'get_pro_players') + @mock.patch.object(EspnFantasyRequests, "get_pro_players") def test_base_league_fetch_players(self, mock_get_players): - with open('tests/hockey/unit/data/player_data.json') as data: + with open("tests/hockey/unit/data/player_data.json") as data: player_data = json.loads(data.read()) mock_get_players.return_value = player_data self.league._fetch_players() - self.assertEqual(self.league.player_map['Charlie Coyle'], 2555315) - self.assertEqual(self.league.player_map[2555315], 'Charlie Coyle') + self.assertEqual(self.league.player_map["Charlie Coyle"], 2555315) + self.assertEqual(self.league.player_map[2555315], "Charlie Coyle") mock_get_players.assert_called_once() - @mock.patch.object(EspnFantasyRequests, 'get_pro_schedule') + @mock.patch.object(EspnFantasyRequests, "get_pro_schedule") def test_base_league_fetch_schedule(self, mock_get_pro_schedule): - with open('tests/hockey/unit/data/pro_schedule.json') as data: + with open("tests/hockey/unit/data/pro_schedule.json") as data: schedule_data = json.loads(data.read()) mock_get_pro_schedule.return_value = schedule_data @@ -56,31 +55,32 @@ def test_base_league_fetch_schedule(self, mock_get_pro_schedule): mock_get_pro_schedule.assert_called_once() def test_base_league_standings(self): - expected_standings = ["Team(Barkko Ruutu)", - "Team(2 Minutes for.. Rooping?)", - "Team(Tyutin in the Staal)", - "Team(Turds of Misery)", - "Team(Fast and Fleuryious)", - "Team(The Return of the Captain)", - "Team(Eichel Scott Paper Company )", - "Team(Took a Dump and Chased)", - "Team(Lafleur Power -)", - "Team(Drop Trou and Shattenkirk)"] - self.league._fetch_teams(self.league_data, TeamClass= Team) + expected_standings = [ + "Team(Barkko Ruutu)", + "Team(2 Minutes for.. Rooping?)", + "Team(Tyutin in the Staal)", + "Team(Turds of Misery)", + "Team(Fast and Fleuryious)", + "Team(The Return of the Captain)", + "Team(Eichel Scott Paper Company )", + "Team(Took a Dump and Chased)", + "Team(Lafleur Power -)", + "Team(Drop Trou and Shattenkirk)", + ] + self.league._fetch_teams(self.league_data, TeamClass=Team) actual_standings = self.league.standings() for i, actual_team in enumerate(actual_standings): self.assertEqual(repr(actual_team), expected_standings[i]) - class HockeyLeagueTest(BaseLeagueTest): def setUp(self): super().setUp() - @mock.patch.object(EspnFantasyRequests, 'get_league_draft') - @mock.patch.object(EspnFantasyRequests, 'get_league') + @mock.patch.object(EspnFantasyRequests, "get_league_draft") + @mock.patch.object(EspnFantasyRequests, "get_league") def test_league(self, mock_league_request, mock_league_draft): mock_league_request.return_value = self.league_data mock_league_draft.return_value = {} @@ -92,21 +92,25 @@ def test_league(self, mock_league_request, mock_league_draft): self.assertEqual(league.year, self.season) mock_league_request.assert_called_once() - @mock.patch.object(EspnFantasyRequests, 'get_league_draft') - @mock.patch.object(EspnFantasyRequests, 'get_league') + @mock.patch.object(EspnFantasyRequests, "get_league_draft") + @mock.patch.object(EspnFantasyRequests, "get_league") def test_league_teams(self, mock_league_request, mock_league_draft): mock_league_draft.return_value = {} mock_league_request.return_value = self.league_data - expected_teams = set(["Team(Barkko Ruutu)", - "Team(2 Minutes for.. Rooping?)", - "Team(Tyutin in the Staal)", - "Team(Turds of Misery)", - "Team(Fast and Fleuryious)", - "Team(The Return of the Captain)", - "Team(Eichel Scott Paper Company )", - "Team(Took a Dump and Chased)", - "Team(Lafleur Power -)", - "Team(Drop Trou and Shattenkirk)"]) + expected_teams = set( + [ + "Team(Barkko Ruutu)", + "Team(2 Minutes for.. Rooping?)", + "Team(Tyutin in the Staal)", + "Team(Turds of Misery)", + "Team(Fast and Fleuryious)", + "Team(The Return of the Captain)", + "Team(Eichel Scott Paper Company )", + "Team(Took a Dump and Chased)", + "Team(Lafleur Power -)", + "Team(Drop Trou and Shattenkirk)", + ] + ) league = HockeyLeague(self.league_id, self.season) actual_teams = set(league.teams) @@ -115,18 +119,20 @@ def test_league_teams(self, mock_league_request, mock_league_draft): self.assertIn(repr(actual_team), expected_teams) mock_league_request.assert_called_once() - @mock.patch.object(EspnFantasyRequests, 'get_league_draft') - @mock.patch.object(EspnFantasyRequests, 'league_get') - @mock.patch.object(EspnFantasyRequests, 'get_league') - def test_league_scoreboard(self, mock_get_league_request, mock_league_get_request, mock_league_draft): - with open('tests/hockey/unit/data/matchup_data.json') as file: + @mock.patch.object(EspnFantasyRequests, "get_league_draft") + @mock.patch.object(EspnFantasyRequests, "league_get") + @mock.patch.object(EspnFantasyRequests, "get_league") + def test_league_scoreboard( + self, mock_get_league_request, mock_league_get_request, mock_league_draft + ): + with open("tests/hockey/unit/data/matchup_data.json") as file: matchup_data = json.loads(file.read()) mock_league_draft.return_value = {} mock_get_league_request.return_value = self.league_data mock_league_get_request.return_value = matchup_data league = HockeyLeague(self.league_id, self.season) - first_expected_matchup = 'Matchup(Team(Drop Trou and Shattenkirk) 9.0 - 1.0 Team(Eichel Scott Paper Company ))' + first_expected_matchup = "Matchup(Team(Drop Trou and Shattenkirk) 9.0 - 1.0 Team(Eichel Scott Paper Company ))" actual_matchups = league.scoreboard() @@ -135,32 +141,34 @@ def test_league_scoreboard(self, mock_get_league_request, mock_league_get_reques mock_get_league_request.assert_called_once() mock_league_get_request.assert_called_once() - @mock.patch.object(EspnFantasyRequests, 'get_league_draft') - @mock.patch.object(EspnFantasyRequests, 'get_league') + @mock.patch.object(EspnFantasyRequests, "get_league_draft") + @mock.patch.object(EspnFantasyRequests, "get_league") def test_league_get_team_data(self, mock_get_league_request, mock_league_draft): mock_league_draft.return_value = {} mock_get_league_request.return_value = self.league_data league = HockeyLeague(self.league_id, self.season) - expected_team = 'Team(The Return of the Captain)' + expected_team = "Team(The Return of the Captain)" actual_team = league.get_team_data(9) self.assertEqual(expected_team, repr(actual_team)) mock_get_league_request.assert_called_once() - @mock.patch.object(EspnFantasyRequests, 'get_league_draft') - @mock.patch.object(EspnFantasyRequests, 'league_get') - @mock.patch.object(EspnFantasyRequests, 'get_league') - def test_league_free_agency(self, mock_get_league_request, mock_league_get_request, mock_league_draft): - with open('tests/hockey/unit/data/free_agent_data.json') as file: + @mock.patch.object(EspnFantasyRequests, "get_league_draft") + @mock.patch.object(EspnFantasyRequests, "league_get") + @mock.patch.object(EspnFantasyRequests, "get_league") + def test_league_free_agency( + self, mock_get_league_request, mock_league_get_request, mock_league_draft + ): + with open("tests/hockey/unit/data/free_agent_data.json") as file: free_agents_data = json.loads(file.read()) mock_league_draft.return_value = {} mock_get_league_request.return_value = self.league_data mock_league_get_request.return_value = free_agents_data league = HockeyLeague(self.league_id, self.season) - first_expected_free_agent = 'Player(Brendan Gallagher)' + first_expected_free_agent = "Player(Brendan Gallagher)" actual_free_agents = league.free_agents() @@ -169,18 +177,22 @@ def test_league_free_agency(self, mock_get_league_request, mock_league_get_reque mock_get_league_request.assert_called_once() mock_league_get_request.assert_called_once() - @mock.patch.object(EspnFantasyRequests, 'get_league_draft') - @mock.patch.object(EspnFantasyRequests, 'league_get') - @mock.patch.object(EspnFantasyRequests, 'get_league') - def test_league_recent_activity(self, mock_get_league_request, mock_league_get_request, mock_league_draft): - with open('tests/hockey/unit/data/recent_activity_data.json') as file: + @mock.patch.object(EspnFantasyRequests, "get_league_draft") + @mock.patch.object(EspnFantasyRequests, "league_get") + @mock.patch.object(EspnFantasyRequests, "get_league") + def test_league_recent_activity( + self, mock_get_league_request, mock_league_get_request, mock_league_draft + ): + with open("tests/hockey/unit/data/recent_activity_data.json") as file: activity_data = json.loads(file.read()) mock_league_draft.return_value = {} mock_get_league_request.return_value = self.league_data mock_league_get_request.return_value = activity_data league = HockeyLeague(self.league_id, self.season) - first_expected_activity = 'Activity((Team(2 Minutes for.. Rooping?),FA ADDED,Jake DeBrusk))' + first_expected_activity = ( + "Activity((Team(2 Minutes for.. Rooping?),FA ADDED,Jake DeBrusk))" + ) actual_activities = league.recent_activity() @@ -189,18 +201,20 @@ def test_league_recent_activity(self, mock_get_league_request, mock_league_get_r mock_get_league_request.assert_called_once() mock_league_get_request.assert_called_once() - @mock.patch.object(EspnFantasyRequests, 'get_league_draft') - @mock.patch.object(EspnFantasyRequests, 'league_get') - @mock.patch.object(EspnFantasyRequests, 'get_league') - def test_league_box_scores(self, mock_get_league_request, mock_league_get_request, mock_league_draft): + @mock.patch.object(EspnFantasyRequests, "get_league_draft") + @mock.patch.object(EspnFantasyRequests, "league_get") + @mock.patch.object(EspnFantasyRequests, "get_league") + def test_league_box_scores( + self, mock_get_league_request, mock_league_get_request, mock_league_draft + ): mock_league_draft.return_value = {} - with open('tests/hockey/unit/data/box_score_data.json') as file: + with open("tests/hockey/unit/data/box_score_data.json") as file: box_score_data = json.loads(file.read()) mock_get_league_request.return_value = self.league_data mock_league_get_request.return_value = box_score_data league = HockeyLeague(self.league_id, self.season) - first_box_score = 'Box Score(12 at Team(2 Minutes for.. Rooping?))' + first_box_score = "Box Score(12 at Team(2 Minutes for.. Rooping?))" actual_box_scores = league.box_scores() diff --git a/tests/hockey/unit/test_player.py b/tests/hockey/unit/test_player.py index fb580c9cb..ceae01982 100644 --- a/tests/hockey/unit/test_player.py +++ b/tests/hockey/unit/test_player.py @@ -7,17 +7,12 @@ class TestPlayer(TestCase): def setUp(self) -> None: - with open('tests/hockey/unit/data/league_data.json') as data: - self.roster_data = json.loads(data.read())['teams'][0]['roster']['entries'] + with open("tests/hockey/unit/data/league_data.json") as data: + self.roster_data = json.loads(data.read())["teams"][0]["roster"]["entries"] def test_player(self): player_input = self.roster_data[0] actual_player = Player(player_input) - self.assertEqual('Player(Taylor Hall)', repr(actual_player)) - self.assertEqual(actual_player.position, 'Left Wing') - - - - - + self.assertEqual("Player(Taylor Hall)", repr(actual_player)) + self.assertEqual(actual_player.position, "Left Wing") diff --git a/tests/hockey/unit/test_team.py b/tests/hockey/unit/test_team.py index e03e0a352..60ce16f07 100644 --- a/tests/hockey/unit/test_team.py +++ b/tests/hockey/unit/test_team.py @@ -6,23 +6,26 @@ class TestHockeyTeam(TestCase): def setUp(self) -> None: - with open('tests/hockey/unit/data/league_data.json') as file: + with open("tests/hockey/unit/data/league_data.json") as file: self.data = json.loads(file.read()) - self.teams = self.data['teams'] - self.schedule = self.data['schedule'] - self.seasonId = self.data['seasonId'] + self.teams = self.data["teams"] + self.schedule = self.data["schedule"] + self.seasonId = self.data["seasonId"] self.year = 2020 - self.team = self.data['teams'][3] - self.team_roster = self.team['roster'] + self.team = self.data["teams"][3] + self.team_roster = self.team["roster"] def test_team(self): - team = Team(self.team, roster= self.team_roster, schedule= self.schedule, year= self.year) - self.assertEqual(team.team_abbrev, 'ESPC') + team = Team( + self.team, roster=self.team_roster, schedule=self.schedule, year=self.year + ) + self.assertEqual(team.team_abbrev, "ESPC") def test_team_roster_df(self): - team = Team(self.team, roster= self.team_roster, schedule= self.schedule, year= self.year) + team = Team( + self.team, roster=self.team_roster, schedule=self.schedule, year=self.year + ) self.assertEqual(len(team.roster), 25) - self.assertEqual(team.roster[0].name, 'Thomas Chabot') - + self.assertEqual(team.roster[0].name, "Thomas Chabot") diff --git a/tests/wbasketball/integration/test_league.py b/tests/wbasketball/integration/test_league.py index 7f7305ed0..81f718ec3 100644 --- a/tests/wbasketball/integration/test_league.py +++ b/tests/wbasketball/integration/test_league.py @@ -1,9 +1,10 @@ from unittest import TestCase from espn_api.wbasketball import League + # Integration test to make sure ESPN's API didn't change class LeagueTest(TestCase): - + # def test_league_init(self): # league = League(1010650954, 2022) @@ -15,7 +16,7 @@ class LeagueTest(TestCase): # self.assertEqual(scores[0].home_final_score, 136.3) # self.assertEqual(scores[0].away_final_score, 224.3) - + def test_league_free_agents(self): league = League(1010650954, 2022) free_agents = league.free_agents() @@ -40,7 +41,7 @@ def test_league_free_agents(self): # def test_past_league(self): # league = League(411647, 2017) - + # self.assertEqual(league.scoringPeriodId, 170) # def test_past_league_scoreboard(self): @@ -49,7 +50,7 @@ def test_league_free_agents(self): # self.assertTrue(scores[0].home_final_score > 0) # self.assertTrue(scores[0].away_final_score > 0) - + def test_blank_league_init(self): blank_league = League(1010650954, 2022, fetch_league=False) - self.assertEqual(len(blank_league.teams), 0) \ No newline at end of file + self.assertEqual(len(blank_league.teams), 0) diff --git a/tests/wbasketball/unit/test_activity.py b/tests/wbasketball/unit/test_activity.py index 0c6748d70..fd7fff27c 100644 --- a/tests/wbasketball/unit/test_activity.py +++ b/tests/wbasketball/unit/test_activity.py @@ -6,268 +6,262 @@ class ActivityTest(TestCase): def setUp(self): """Set up test fixtures""" self.player_map = { - 1001: 'Player One', - 1002: 'Player Two', - 1003: 'Player Three', + 1001: "Player One", + 1002: "Player Two", + 1003: "Player Three", } - + self.team_data = { - 'team1': 'Team One', - 'team2': 'Team Two', - 'team3': 'Team Three', + "team1": "Team One", + "team2": "Team Two", + "team3": "Team Three", } - - self.get_team_data = lambda team_id: self.team_data.get(team_id, '') + + self.get_team_data = lambda team_id: self.team_data.get(team_id, "") def test_activity_init_empty_messages(self): """Test Activity initialization with empty messages""" - data = { - 'date': '2023-01-01', - 'messages': [] - } - + data = {"date": "2023-01-01", "messages": []} + activity = Activity(data, self.player_map, self.get_team_data) - - self.assertEqual(activity.date, '2023-01-01') + + self.assertEqual(activity.date, "2023-01-01") self.assertEqual(len(activity.actions), 0) def test_activity_init_with_fa_added(self): """Test Activity with FA_ADDED message (type 178)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(team, 'Team One') - self.assertEqual(action, 'FA ADDED') - self.assertEqual(player, 'Player One') + self.assertEqual(team, "Team One") + self.assertEqual(action, "FA ADDED") + self.assertEqual(player, "Player One") def test_activity_init_with_waiver_added(self): """Test Activity with WAIVER_ADDED message (type 180)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 180, - 'to': 'team1', - 'targetId': 1002, + "messageTypeId": 180, + "to": "team1", + "targetId": 1002, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(team, 'Team One') - self.assertEqual(action, 'WAIVER ADDED') - self.assertEqual(player, 'Player Two') + self.assertEqual(team, "Team One") + self.assertEqual(action, "WAIVER ADDED") + self.assertEqual(player, "Player Two") def test_activity_init_with_dropped(self): """Test Activity with DROPPED message (type 179)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 179, - 'to': 'team2', - 'targetId': 1001, + "messageTypeId": 179, + "to": "team2", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(team, 'Team Two') - self.assertEqual(action, 'DROPPED') - self.assertEqual(player, 'Player One') + self.assertEqual(team, "Team Two") + self.assertEqual(action, "DROPPED") + self.assertEqual(player, "Player One") def test_activity_init_with_traded(self): """Test Activity with TRADED message (type 244)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 244, - 'from': 'team1', - 'targetId': 1003, + "messageTypeId": 244, + "from": "team1", + "targetId": 1003, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(team, 'Team One') - self.assertEqual(action, 'TRADED') - self.assertEqual(player, 'Player Three') + self.assertEqual(team, "Team One") + self.assertEqual(action, "TRADED") + self.assertEqual(player, "Player Three") def test_activity_init_with_unknown_player(self): """Test Activity with player not in player_map""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 9999, # Player not in map + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 9999, # Player not in map } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(player, '') + self.assertEqual(player, "") def test_activity_init_with_unknown_message_type(self): """Test Activity with unknown message type""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 999, # Unknown message type - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 999, # Unknown message type + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + # Unknown message type still adds action with UNKNOWN action self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(action, 'UNKNOWN') + self.assertEqual(action, "UNKNOWN") def test_activity_repr_with_actions(self): """Test Activity __repr__ with actions""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + repr_str = repr(activity) - self.assertIn('Activity', repr_str) - self.assertIn('Team One', repr_str) - self.assertIn('FA ADDED', repr_str) - self.assertIn('Player One', repr_str) + self.assertIn("Activity", repr_str) + self.assertIn("Team One", repr_str) + self.assertIn("FA ADDED", repr_str) + self.assertIn("Player One", repr_str) def test_activity_repr_empty_actions(self): """Test Activity __repr__ with no actions""" - data = { - 'date': '2023-01-01', - 'messages': [] - } - + data = {"date": "2023-01-01", "messages": []} + activity = Activity(data, self.player_map, self.get_team_data) - + repr_str = repr(activity) - self.assertEqual(repr_str, 'Activity()') + self.assertEqual(repr_str, "Activity()") def test_activity_multiple_messages(self): """Test Activity with multiple messages""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 178, - 'from': 'team1', - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 178, + "from": "team1", + "to": "team1", + "targetId": 1001, }, { - 'messageTypeId': 179, - 'to': 'team2', - 'targetId': 1002, - } - ] + "messageTypeId": 179, + "to": "team2", + "targetId": 1002, + }, + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 2) - self.assertEqual(activity.actions[0][1], 'FA ADDED') - self.assertEqual(activity.actions[1][1], 'DROPPED') + self.assertEqual(activity.actions[0][1], "FA ADDED") + self.assertEqual(activity.actions[1][1], "DROPPED") def test_activity_with_type_239_dropped(self): """Test Activity with type 239 (DROPPED)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 239, - 'for': 'team1', - 'targetId': 1001, + "messageTypeId": 239, + "for": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(action, 'DROPPED') + self.assertEqual(action, "DROPPED") def test_activity_with_type_181_dropped(self): """Test Activity with type 181 (DROPPED)""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 181, - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 181, + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + self.assertEqual(len(activity.actions), 1) team, action, player = activity.actions[0] - self.assertEqual(action, 'DROPPED') + self.assertEqual(action, "DROPPED") def test_activity_all_always_appends(self): """Test that Activity appends action regardless of whether it's UNKNOWN""" data = { - 'date': '2023-01-01', - 'messages': [ + "date": "2023-01-01", + "messages": [ { - 'messageTypeId': 188, # Not in wbasketball ACTIVITY_MAP - 'to': 'team1', - 'targetId': 1001, + "messageTypeId": 188, # Not in wbasketball ACTIVITY_MAP + "to": "team1", + "targetId": 1001, } - ] + ], } - + activity = Activity(data, self.player_map, self.get_team_data) - + # wbasketball version appends all messages self.assertEqual(len(activity.actions), 1) - self.assertEqual(activity.actions[0][1], 'UNKNOWN') + self.assertEqual(activity.actions[0][1], "UNKNOWN") diff --git a/tests/wbasketball/unit/test_box_score.py b/tests/wbasketball/unit/test_box_score.py index c335d9802..2a6076761 100644 --- a/tests/wbasketball/unit/test_box_score.py +++ b/tests/wbasketball/unit/test_box_score.py @@ -2,148 +2,152 @@ from espn_api.wbasketball.box_score import BoxScore -def _make_box_score_data(home_team_id=1, away_team_id=2, winner='UNDECIDED', - home_score=100.0, away_score=95.0, by_matchup=True, - home_projected=105.0, away_projected=98.0, include_away=True): +def _make_box_score_data( + home_team_id=1, + away_team_id=2, + winner="UNDECIDED", + home_score=100.0, + away_score=95.0, + by_matchup=True, + home_projected=105.0, + away_projected=98.0, + include_away=True, +): """Helper function to create box score data""" data = { - 'winner': winner, - 'home': { - 'teamId': home_team_id, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': home_score, - 'entries': [] - }, - 'totalPointsLive': home_score if by_matchup else None, - 'totalProjectedPointsLive': home_projected if by_matchup else None - } + "winner": winner, + "home": { + "teamId": home_team_id, + "rosterForMatchupPeriod": {"appliedStatTotal": home_score, "entries": []}, + "totalPointsLive": home_score if by_matchup else None, + "totalProjectedPointsLive": home_projected if by_matchup else None, + }, } - + if include_away: - data['away'] = { - 'teamId': away_team_id, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': away_score, - 'entries': [] - }, - 'totalPointsLive': away_score if by_matchup else None, - 'totalProjectedPointsLive': away_projected if by_matchup else None + data["away"] = { + "teamId": away_team_id, + "rosterForMatchupPeriod": {"appliedStatTotal": away_score, "entries": []}, + "totalPointsLive": away_score if by_matchup else None, + "totalProjectedPointsLive": away_projected if by_matchup else None, } - + return data class BoxScoreTest(TestCase): - + def test_box_score_init_basic(self): """Test basic BoxScore initialization""" data = _make_box_score_data() pro_schedule = {} - + box_score = BoxScore(data, pro_schedule, True, 2023) - + self.assertEqual(box_score.home_team, 1) self.assertEqual(box_score.away_team, 2) - self.assertEqual(box_score.winner, 'UNDECIDED') + self.assertEqual(box_score.winner, "UNDECIDED") def test_box_score_home_team_id(self): """Test home team ID is set correctly""" data = _make_box_score_data(home_team_id=5) - + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(box_score.home_team, 5) def test_box_score_away_team_id(self): """Test away team ID is set correctly""" data = _make_box_score_data(away_team_id=8) - + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(box_score.away_team, 8) def test_box_score_winner(self): """Test winner field is set correctly""" - data = _make_box_score_data(winner='HOME') - + data = _make_box_score_data(winner="HOME") + box_score = BoxScore(data, {}, True, 2023) - - self.assertEqual(box_score.winner, 'HOME') + + self.assertEqual(box_score.winner, "HOME") def test_box_score_scores_by_matchup(self): """Test scores when by_matchup is True""" data = _make_box_score_data(home_score=100.0, away_score=95.0, by_matchup=True) - + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(box_score.home_score, 100.0) self.assertEqual(box_score.away_score, 95.0) def test_box_score_scores_not_by_matchup(self): """Test scores when by_matchup is False""" data = { - 'winner': 'UNDECIDED', - 'home': { - 'teamId': 1, - 'rosterForCurrentScoringPeriod': { - 'appliedStatTotal': 102.5, - 'entries': [] - } + "winner": "UNDECIDED", + "home": { + "teamId": 1, + "rosterForCurrentScoringPeriod": { + "appliedStatTotal": 102.5, + "entries": [], + }, + }, + "away": { + "teamId": 2, + "rosterForCurrentScoringPeriod": { + "appliedStatTotal": 98.5, + "entries": [], + }, }, - 'away': { - 'teamId': 2, - 'rosterForCurrentScoringPeriod': { - 'appliedStatTotal': 98.5, - 'entries': [] - } - } } - + box_score = BoxScore(data, {}, False, 2023) - + self.assertEqual(box_score.home_score, 102.5) self.assertEqual(box_score.away_score, 98.5) def test_box_score_projected_scores_by_matchup(self): """Test projected scores when by_matchup is True""" - data = _make_box_score_data(home_projected=105.5, away_projected=98.2, by_matchup=True) - + data = _make_box_score_data( + home_projected=105.5, away_projected=98.2, by_matchup=True + ) + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(box_score.home_projected, 105.5) self.assertEqual(box_score.away_projected, 98.2) def test_box_score_projected_scores_default_not_by_matchup(self): """Test projected scores default to -1 when by_matchup is False""" data = { - 'winner': 'UNDECIDED', - 'home': { - 'teamId': 1, - 'rosterForCurrentScoringPeriod': { - 'appliedStatTotal': 100.0, - 'entries': [] - } + "winner": "UNDECIDED", + "home": { + "teamId": 1, + "rosterForCurrentScoringPeriod": { + "appliedStatTotal": 100.0, + "entries": [], + }, + }, + "away": { + "teamId": 2, + "rosterForCurrentScoringPeriod": { + "appliedStatTotal": 95.0, + "entries": [], + }, }, - 'away': { - 'teamId': 2, - 'rosterForCurrentScoringPeriod': { - 'appliedStatTotal': 95.0, - 'entries': [] - } - } } - + box_score = BoxScore(data, {}, False, 2023) - + self.assertEqual(box_score.home_projected, -1) self.assertEqual(box_score.away_projected, -1) def test_box_score_no_away_team_bye_week(self): """Test BoxScore with no away team (bye week)""" data = _make_box_score_data(include_away=False) - + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(box_score.away_team, 0) self.assertEqual(box_score.away_score, 0) self.assertEqual(box_score.away_projected, -1) @@ -152,137 +156,116 @@ def test_box_score_no_away_team_bye_week(self): def test_box_score_home_lineup_empty(self): """Test BoxScore home lineup is empty when no entries""" data = _make_box_score_data() - + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(len(box_score.home_lineup), 0) def test_box_score_away_lineup_empty(self): """Test BoxScore away lineup is empty when no entries""" data = _make_box_score_data() - + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(len(box_score.away_lineup), 0) def test_box_score_repr_with_both_teams(self): """Test BoxScore repr with both home and away teams""" data = _make_box_score_data(home_team_id=5, away_team_id=8, include_away=True) - + box_score = BoxScore(data, {}, True, 2023) - + repr_str = repr(box_score) - self.assertIn('Box Score', repr_str) - self.assertIn('8', repr_str) # Away team - self.assertIn('5', repr_str) # Home team - self.assertIn('at', repr_str) + self.assertIn("Box Score", repr_str) + self.assertIn("8", repr_str) # Away team + self.assertIn("5", repr_str) # Home team + self.assertIn("at", repr_str) def test_box_score_repr_with_bye_week(self): """Test BoxScore repr when away team is bye""" data = _make_box_score_data(include_away=False) - + box_score = BoxScore(data, {}, True, 2023) - + repr_str = repr(box_score) - self.assertIn('BYE', repr_str) - self.assertIn('at', repr_str) + self.assertIn("BYE", repr_str) + self.assertIn("at", repr_str) def test_box_score_score_rounding(self): """Test that scores are rounded to 2 decimals""" data = { - 'winner': 'UNDECIDED', - 'home': { - 'teamId': 1, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 100.12345, - 'entries': [] + "winner": "UNDECIDED", + "home": { + "teamId": 1, + "rosterForMatchupPeriod": { + "appliedStatTotal": 100.12345, + "entries": [], }, - 'totalPointsLive': 100.12345 + "totalPointsLive": 100.12345, + }, + "away": { + "teamId": 2, + "rosterForMatchupPeriod": {"appliedStatTotal": 95.98765, "entries": []}, + "totalPointsLive": 95.98765, }, - 'away': { - 'teamId': 2, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 95.98765, - 'entries': [] - }, - 'totalPointsLive': 95.98765 - } } - + box_score = BoxScore(data, {}, True, 2023) - + self.assertEqual(box_score.home_score, 100.12) self.assertEqual(box_score.away_score, 95.99) def test_box_score_default_winner(self): """Test default winner when not provided""" data = { - 'home': { - 'teamId': 1, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 100.0, - 'entries': [] - }, - 'totalPointsLive': 100.0 + "home": { + "teamId": 1, + "rosterForMatchupPeriod": {"appliedStatTotal": 100.0, "entries": []}, + "totalPointsLive": 100.0, + }, + "away": { + "teamId": 2, + "rosterForMatchupPeriod": {"appliedStatTotal": 95.0, "entries": []}, + "totalPointsLive": 95.0, }, - 'away': { - 'teamId': 2, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 95.0, - 'entries': [] - }, - 'totalPointsLive': 95.0 - } } - + box_score = BoxScore(data, {}, True, 2023) - - self.assertEqual(box_score.winner, 'UNDECIDED') + + self.assertEqual(box_score.winner, "UNDECIDED") def test_box_score_home_projected_default(self): """Test home projected defaults to -1""" data = { - 'winner': 'UNDECIDED', - 'home': { - 'teamId': 1, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 100.0, - 'entries': [] - } + "winner": "UNDECIDED", + "home": { + "teamId": 1, + "rosterForMatchupPeriod": {"appliedStatTotal": 100.0, "entries": []}, + }, + "away": { + "teamId": 2, + "rosterForMatchupPeriod": {"appliedStatTotal": 95.0, "entries": []}, }, - 'away': { - 'teamId': 2, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 95.0, - 'entries': [] - } - } } - + box_score = BoxScore(data, {}, False, 2023) - + self.assertEqual(box_score.home_projected, -1) def test_box_score_away_projected_default(self): """Test away projected defaults to -1""" data = { - 'winner': 'UNDECIDED', - 'home': { - 'teamId': 1, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 100.0, - 'entries': [] - } + "winner": "UNDECIDED", + "home": { + "teamId": 1, + "rosterForMatchupPeriod": {"appliedStatTotal": 100.0, "entries": []}, + }, + "away": { + "teamId": 2, + "rosterForMatchupPeriod": {"appliedStatTotal": 95.0, "entries": []}, }, - 'away': { - 'teamId': 2, - 'rosterForMatchupPeriod': { - 'appliedStatTotal': 95.0, - 'entries': [] - } - } } - + box_score = BoxScore(data, {}, False, 2023) - + self.assertEqual(box_score.away_projected, -1) diff --git a/tests/wbasketball/unit/test_player.py b/tests/wbasketball/unit/test_player.py index 20de5e2d3..c8dfb8e02 100644 --- a/tests/wbasketball/unit/test_player.py +++ b/tests/wbasketball/unit/test_player.py @@ -3,48 +3,58 @@ from espn_api.wbasketball.constant import POSITION_MAP, PRO_TEAM_MAP, STATS_MAP -def _make_player_data(full_name='Test Player', player_id=1234, default_position_id=1, - lineup_slot_id=0, eligible_slots=None, pro_team_id=3, - acquisition_type='DRAFT', injury_status='ACTIVE', stats=None, - player_extras=None): +def _make_player_data( + full_name="Test Player", + player_id=1234, + default_position_id=1, + lineup_slot_id=0, + eligible_slots=None, + pro_team_id=3, + acquisition_type="DRAFT", + injury_status="ACTIVE", + stats=None, + player_extras=None, +): """Helper function to create player data""" if eligible_slots is None: eligible_slots = [lineup_slot_id] if stats is None: stats = [] - + return { - 'fullName': full_name, - 'id': player_id, - 'defaultPositionId': default_position_id, - 'lineupSlotId': lineup_slot_id, - 'eligibleSlots': eligible_slots, - 'acquisitionType': acquisition_type, - 'acquisitionDate': 1700000000000, - 'proTeamId': pro_team_id, - 'injuryStatus': injury_status, - 'playerPoolEntry': { - 'player': { - 'fullName': full_name, - 'id': player_id, - 'injuryStatus': injury_status, - 'injured': False, - 'stats': stats, - **(player_extras or {}) + "fullName": full_name, + "id": player_id, + "defaultPositionId": default_position_id, + "lineupSlotId": lineup_slot_id, + "eligibleSlots": eligible_slots, + "acquisitionType": acquisition_type, + "acquisitionDate": 1700000000000, + "proTeamId": pro_team_id, + "injuryStatus": injury_status, + "playerPoolEntry": { + "player": { + "fullName": full_name, + "id": player_id, + "injuryStatus": injury_status, + "injured": False, + "stats": stats, + **(player_extras or {}), } - } + }, } class PlayerTest(TestCase): - + def test_player_basic_init(self): """Test basic Player initialization""" - data = _make_player_data(full_name='Breanna Stewart', player_id=1001, default_position_id=2) - + data = _make_player_data( + full_name="Breanna Stewart", player_id=1001, default_position_id=2 + ) + player = Player(data, 2023) - - self.assertEqual(player.name, 'Breanna Stewart') + + self.assertEqual(player.name, "Breanna Stewart") self.assertEqual(player.playerId, 1001) self.assertEqual(player.position, POSITION_MAP[2]) @@ -52,7 +62,7 @@ def test_player_position_mapping(self): """Test that player positions are correctly mapped""" # Test various positions positions = [1, 2, 3, 4, 5] - + for pos_id in positions: data = _make_player_data(default_position_id=pos_id) player = Player(data, 2023) @@ -61,8 +71,8 @@ def test_player_position_mapping(self): def test_player_pro_team_mapping(self): """Test that pro teams are correctly mapped""" # Test a few team IDs that are in wbasketball PRO_TEAM_MAP - pro_team_data = [(3, 'Dal'), (5, 'Ind'), (6, 'LA'), (8, 'Min')] - + pro_team_data = [(3, "Dal"), (5, "Ind"), (6, "LA"), (8, "Min")] + for team_id, expected_team in pro_team_data: data = _make_player_data(pro_team_id=team_id) player = Player(data, 2023) @@ -70,15 +80,15 @@ def test_player_pro_team_mapping(self): def test_player_acquisition_type(self): """Test player acquisition type""" - data = _make_player_data(acquisition_type='WAIVER') + data = _make_player_data(acquisition_type="WAIVER") player = Player(data, 2023) - self.assertEqual(player.acquisitionType, 'WAIVER') + self.assertEqual(player.acquisitionType, "WAIVER") def test_player_injury_status(self): """Test player injury status""" - data = _make_player_data(injury_status='OUT') + data = _make_player_data(injury_status="OUT") player = Player(data, 2023) - self.assertEqual(player.injuryStatus, 'OUT') + self.assertEqual(player.injuryStatus, "OUT") def test_player_eligible_slots(self): """Test player eligible slots""" @@ -90,7 +100,7 @@ def test_player_lineup_slot(self): """Test player lineup slot""" data = _make_player_data(lineup_slot_id=5) player = Player(data, 2023) - self.assertEqual(player.lineupSlot, POSITION_MAP.get(5, '')) + self.assertEqual(player.lineupSlot, POSITION_MAP.get(5, "")) def test_player_initial_empty_stats(self): """Test that player initializes with empty stats dict""" @@ -119,37 +129,37 @@ def test_player_projected_points_zero(self): def test_player_repr(self): """Test player string representation""" - data = _make_player_data(full_name='Jewell Loyd') + data = _make_player_data(full_name="Jewell Loyd") player = Player(data, 2023) - self.assertEqual(repr(player), 'Player(Jewell Loyd)') + self.assertEqual(repr(player), "Player(Jewell Loyd)") def test_player_with_stats(self): """Test player with stats for the same year""" stats = [ { - 'id': '0010', - 'appliedTotal': 50.0, - 'appliedAverage': 25.0, - 'stats': {'0': 100, '1': 5}, - 'averageStats': {'0': 50, '1': 2.5} + "id": "0010", + "appliedTotal": 50.0, + "appliedAverage": 25.0, + "stats": {"0": 100, "1": 5}, + "averageStats": {"0": 50, "1": 2.5}, } ] - + data = _make_player_data(stats=stats) player = Player(data, 2023) - - self.assertIn('10', player.stats) - self.assertEqual(player.stats['10']['applied_total'], 50.0) + + self.assertIn("10", player.stats) + self.assertEqual(player.stats["10"]["applied_total"], 50.0) def test_player_injured_flag(self): """Test player injured flag""" - data = _make_player_data(player_extras={'injured': True}) + data = _make_player_data(player_extras={"injured": True}) player = Player(data, 2023) self.assertTrue(player.injured) def test_player_not_injured(self): """Test player not injured""" - data = _make_player_data(player_extras={'injured': False}) + data = _make_player_data(player_extras={"injured": False}) player = Player(data, 2023) self.assertFalse(player.injured) @@ -157,92 +167,92 @@ def test_player_stat_id_pretty_total(self): """Test stat ID pretty formatting for total""" data = _make_player_data() player = Player(data, 2023) - + # Test ID '0010' -> '10' - self.assertEqual(player._stat_id_pretty('0010'), '10') + self.assertEqual(player._stat_id_pretty("0010"), "10") def test_player_stat_id_pretty_projected(self): """Test stat ID pretty formatting for projected""" data = _make_player_data() player = Player(data, 2023) - + # Test ID '1010' -> '10_projected' - self.assertEqual(player._stat_id_pretty('1010'), '10_projected') + self.assertEqual(player._stat_id_pretty("1010"), "10_projected") def test_player_stat_id_pretty_unknown(self): """Test stat ID pretty formatting for unknown type""" data = _make_player_data() player = Player(data, 2023) - + # Test unknown ID format - self.assertEqual(player._stat_id_pretty('9910'), '10') + self.assertEqual(player._stat_id_pretty("9910"), "10") def test_player_with_pool_entry_nested(self): """Test player with playerPoolEntry nested data""" nested_data = { - 'fullName': 'Test Player', - 'id': 1234, - 'defaultPositionId': 1, - 'lineupSlotId': 0, - 'eligibleSlots': [0], - 'acquisitionType': 'DRAFT', - 'acquisitionDate': 1700000000000, - 'proTeamId': 3, - 'injuryStatus': 'ACTIVE', - 'playerPoolEntry': { - 'player': { - 'fullName': 'Test Player', - 'id': 1234, - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'stats': [] + "fullName": "Test Player", + "id": 1234, + "defaultPositionId": 1, + "lineupSlotId": 0, + "eligibleSlots": [0], + "acquisitionType": "DRAFT", + "acquisitionDate": 1700000000000, + "proTeamId": 3, + "injuryStatus": "ACTIVE", + "playerPoolEntry": { + "player": { + "fullName": "Test Player", + "id": 1234, + "injuryStatus": "ACTIVE", + "injured": False, + "stats": [], } - } + }, } - + player = Player(nested_data, 2023) - self.assertEqual(player.name, 'Test Player') + self.assertEqual(player.name, "Test Player") self.assertEqual(player.playerId, 1234) def test_player_with_player_directly_in_data(self): """Test player when 'player' is directly in data (not nested in playerPoolEntry)""" data = { - 'fullName': 'Direct Player', - 'id': 5678, - 'defaultPositionId': 2, - 'lineupSlotId': 1, - 'eligibleSlots': [1], - 'acquisitionType': 'WAIVER', - 'acquisitionDate': 1700000000000, - 'proTeamId': 5, - 'injuryStatus': 'ACTIVE', - 'player': { - 'fullName': 'Direct Player', - 'id': 5678, - 'injuryStatus': 'ACTIVE', - 'injured': False, - 'stats': [] - } + "fullName": "Direct Player", + "id": 5678, + "defaultPositionId": 2, + "lineupSlotId": 1, + "eligibleSlots": [1], + "acquisitionType": "WAIVER", + "acquisitionDate": 1700000000000, + "proTeamId": 5, + "injuryStatus": "ACTIVE", + "player": { + "fullName": "Direct Player", + "id": 5678, + "injuryStatus": "ACTIVE", + "injured": False, + "stats": [], + }, } - + player = Player(data, 2023) - self.assertEqual(player.name, 'Direct Player') + self.assertEqual(player.name, "Direct Player") self.assertEqual(player.playerId, 5678) def test_player_stats_with_no_average_stats(self): """Test player stats without averageStats key""" stats = [ { - 'id': '0010', - 'appliedTotal': 50.0, - 'appliedAverage': 25.0, - 'stats': {'0': 100, '1': 5} + "id": "0010", + "appliedTotal": 50.0, + "appliedAverage": 25.0, + "stats": {"0": 100, "1": 5}, } ] - + data = _make_player_data(stats=stats) player = Player(data, 2023) - - self.assertIn('10', player.stats) - self.assertIsNone(player.stats['10'].get('avg')) - self.assertIsNone(player.stats['10'].get('total')) + + self.assertIn("10", player.stats) + self.assertIsNone(player.stats["10"].get("avg")) + self.assertIsNone(player.stats["10"].get("total"))