Jon Solow commited on
Commit
99d7007
·
1 Parent(s): 23f37fe

Copy yahoo_client from yahoo-ff-dev

Browse files
Files changed (1) hide show
  1. src/yahoo_client.py +164 -0
src/yahoo_client.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from pydantic import BaseModel
3
+ import re
4
+ from typing import List, Mapping, Tuple, Union
5
+ import xml.etree.ElementTree as ET
6
+ from yahoo_oauth import OAuth2
7
+
8
+
9
+ class LeagueSettings(BaseModel):
10
+ league_key: str
11
+ league_id: str
12
+ name: str
13
+ num_teams: int
14
+ current_week: int
15
+ season: int
16
+ playoff_start_week: int
17
+ num_playoff_teams: int
18
+ num_playoff_consolation_teams: int
19
+
20
+ @classmethod
21
+ def from_xml(cls, xml_settings: ET.Element) -> "LeagueSettings":
22
+ base_fields_list = [
23
+ "league_key",
24
+ "league_id",
25
+ "name",
26
+ "num_teams",
27
+ "current_week",
28
+ "season",
29
+ ]
30
+ base_fields_dict = {f: xml_settings.findtext(f"./league/{f}") for f in base_fields_list}
31
+
32
+ settings_fields_list = [
33
+ "playoff_start_week",
34
+ "num_playoff_teams",
35
+ "num_playoff_consolation_teams",
36
+ ]
37
+ settings_fields_dict = {f: xml_settings.findtext(f"./league/settings/{f}") for f in settings_fields_list}
38
+ league_settings_dict = {**base_fields_dict, **settings_fields_dict}
39
+ return cls(
40
+ league_key=league_settings_dict["league_key"] or "",
41
+ league_id=league_settings_dict["league_id"] or "",
42
+ name=league_settings_dict["name"] or "",
43
+ num_teams=int(league_settings_dict["num_teams"] or 0),
44
+ current_week=int(league_settings_dict["current_week"] or 0),
45
+ season=int(league_settings_dict["season"] or 0),
46
+ playoff_start_week=int(league_settings_dict["playoff_start_week"] or 0),
47
+ num_playoff_teams=int(league_settings_dict["num_playoff_teams"] or 0),
48
+ num_playoff_consolation_teams=int(league_settings_dict["num_playoff_consolation_teams"] or 0),
49
+ )
50
+
51
+
52
+ class YahooFantasyClient:
53
+ def get_new_oauth_from_json(self) -> OAuth2:
54
+ return OAuth2(None, None, from_file=self.client_json_path)
55
+
56
+ def __init__(self, client_json_path: str):
57
+ self.client_json_path = client_json_path
58
+ self.oauth: OAuth2 = self.get_new_oauth_from_json()
59
+
60
+ def authorize_yahoo_from_client_json(self) -> None:
61
+ if not self.oauth.token_is_valid():
62
+ self.oauth.refresh_access_token()
63
+
64
+ def yahoo_request_to_xml(self, url: str) -> Tuple[ET.Element, str]:
65
+ self.authorize_yahoo_from_client_json()
66
+ r = self.oauth.session.get(url)
67
+
68
+ xmlstring = r.text
69
+ xmlstring = re.sub(' xmlns="[^"]+"', "", xmlstring, count=1)
70
+ root = ET.fromstring(xmlstring)
71
+ return root, xmlstring
72
+
73
+ def find_all_leagues_for_logged_in_user(self) -> List[str]:
74
+ url = "https://fantasysports.yahooapis.com/fantasy/v2/users;use_login=1/games;game_keys=nfl/leagues"
75
+ root, _ = self.yahoo_request_to_xml(url)
76
+ league_keys = list(
77
+ filter(None, [x.text for x in root.findall("./users/user/games/game/leagues/league/league_key")])
78
+ )
79
+ return league_keys
80
+
81
+ def parse_matchup(self, matchup: ET.Element, match_index: int) -> List[Mapping[str, Union[str, float]]]:
82
+ matchup_info = Matchup.from_xml(matchup, match_index)
83
+ return matchup_info.to_list_team_dict()
84
+
85
+ def parse_league_settings(self, league_key: str) -> LeagueSettings:
86
+ url = f"https://fantasysports.yahooapis.com/fantasy/v2/league/{league_key}/settings"
87
+ league_settings, _ = self.yahoo_request_to_xml(url)
88
+ parsed_league_settings = LeagueSettings.from_xml(league_settings)
89
+ return parsed_league_settings
90
+
91
+ def parse_weeks_matchups(self, week: str, league_key: str) -> List[Mapping[str, Union[str, float]]]:
92
+ url = f"https://fantasysports.yahooapis.com/fantasy/v2/leagues;league_keys={league_key}/scoreboard;week={week}"
93
+ week_scoreboard, _ = self.yahoo_request_to_xml(url)
94
+ week_matchups = week_scoreboard.findall("./leagues/league/scoreboard/matchups/matchup")
95
+ weekly_scores = []
96
+ for match_index, matchup in enumerate(week_matchups):
97
+ matchup_result = self.parse_matchup(matchup, match_index)
98
+ weekly_scores.extend(matchup_result)
99
+ return weekly_scores
100
+
101
+ def full_schedule_dataframe(self, league_key: str) -> pd.DataFrame:
102
+ league_settings = self.parse_league_settings(league_key)
103
+ all_weeks = ",".join([str(w) for w in range(1, league_settings.playoff_start_week)])
104
+ df = pd.DataFrame(self.parse_weeks_matchups(week=all_weeks, league_key=league_key))
105
+ return df
106
+
107
+
108
+ class MatchupTeam(BaseModel):
109
+ team_id: str
110
+ team_name: str
111
+ team_points: float
112
+ win_probability: float
113
+ team_projected_points: float
114
+
115
+ @classmethod
116
+ def from_xml(cls, xml_matchup_team: ET.Element) -> "MatchupTeam":
117
+ team_id = xml_matchup_team.findtext("./team_id")
118
+ team_name = xml_matchup_team.findtext("./name")
119
+ team_points = xml_matchup_team.findtext("./team_points/total")
120
+ win_probability = xml_matchup_team.findtext("./win_probability")
121
+ team_projected_points = xml_matchup_team.findtext("./team_projected_points/total")
122
+ return cls(
123
+ team_id=team_id or "",
124
+ team_name=team_name or "",
125
+ team_points=float(team_points or 0),
126
+ win_probability=float(win_probability or 0),
127
+ team_projected_points=float(team_projected_points or 0),
128
+ )
129
+
130
+
131
+ class Matchup(BaseModel):
132
+ matchup_status: str
133
+ is_playoffs: str
134
+ is_consolation: str
135
+ week: float
136
+ match_index: int
137
+ matchup_teams: List[MatchupTeam]
138
+
139
+ @classmethod
140
+ def from_xml(cls, xml_matchup: ET.Element, match_index: int) -> "Matchup":
141
+ matchup_status = xml_matchup.findtext("./status")
142
+ is_playoffs = xml_matchup.findtext("./is_playoffs")
143
+ is_consolation = xml_matchup.findtext("./is_consolation")
144
+ week = xml_matchup.findtext("./week")
145
+
146
+ xml_matchup_teams = xml_matchup.findall("./teams/team")
147
+ matchup_teams = [MatchupTeam.from_xml(team) for team in xml_matchup_teams]
148
+ return cls(
149
+ matchup_status=matchup_status or "",
150
+ is_playoffs=is_playoffs or "",
151
+ is_consolation=is_consolation or "",
152
+ week=float(week or 0),
153
+ match_index=match_index,
154
+ matchup_teams=matchup_teams,
155
+ )
156
+
157
+ def to_list_team_dict(self) -> List[Mapping[str, Union[str, float]]]:
158
+ matchup_info_dict = self.dict(exclude={"matchup_teams"})
159
+ out_list: List[Mapping[str, Union[str, float]]] = []
160
+ for team in self.matchup_teams:
161
+ team_dict = team.dict()
162
+ team_dict.update(matchup_info_dict)
163
+ out_list.append(team_dict)
164
+ return out_list