impectPy 2.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- impectPy/__init__.py +19 -0
- impectPy/access_token.py +38 -0
- impectPy/config.py +4 -0
- impectPy/events.py +516 -0
- impectPy/helpers.py +284 -0
- impectPy/impect.py +132 -0
- impectPy/iteration_averages.py +382 -0
- impectPy/iterations.py +93 -0
- impectPy/match_info.py +647 -0
- impectPy/matches.py +164 -0
- impectPy/matchsums.py +504 -0
- impectPy/player_profile_scores.py +260 -0
- impectPy/player_scores.py +588 -0
- impectPy/set_pieces.py +281 -0
- impectPy/squad_ratings.py +129 -0
- impectPy/squad_scores.py +359 -0
- impectPy/xml.py +990 -0
- impectPy-2.4.4.dist-info/LICENSE.md +21 -0
- impectPy-2.4.4.dist-info/METADATA +424 -0
- impectPy-2.4.4.dist-info/RECORD +22 -0
- impectPy-2.4.4.dist-info/WHEEL +5 -0
- impectPy-2.4.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# load packages
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import requests
|
|
4
|
+
from impectPy.helpers import RateLimitedAPI, unnest_mappings_df
|
|
5
|
+
from .iterations import getIterationsFromHost
|
|
6
|
+
|
|
7
|
+
# define the allowed positions
|
|
8
|
+
allowed_positions = [
|
|
9
|
+
"GOALKEEPER",
|
|
10
|
+
"LEFT_WINGBACK_DEFENDER",
|
|
11
|
+
"RIGHT_WINGBACK_DEFENDER",
|
|
12
|
+
"CENTRAL_DEFENDER",
|
|
13
|
+
"DEFENSE_MIDFIELD",
|
|
14
|
+
"CENTRAL_MIDFIELD",
|
|
15
|
+
"ATTACKING_MIDFIELD",
|
|
16
|
+
"LEFT_WINGER",
|
|
17
|
+
"RIGHT_WINGER",
|
|
18
|
+
"CENTER_FORWARD"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
######
|
|
22
|
+
#
|
|
23
|
+
# This function returns a pandas dataframe that contains all profile scores
|
|
24
|
+
# for a given iteration and a given set of positions per player
|
|
25
|
+
#
|
|
26
|
+
######
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def getPlayerProfileScores(
|
|
30
|
+
iteration: int, positions: list, token: str, session: requests.Session = requests.Session()
|
|
31
|
+
) -> pd.DataFrame:
|
|
32
|
+
|
|
33
|
+
# create an instance of RateLimitedAPI
|
|
34
|
+
connection = RateLimitedAPI(session)
|
|
35
|
+
|
|
36
|
+
# construct header with access token
|
|
37
|
+
connection.session.headers.update({"Authorization": f"Bearer {token}"})
|
|
38
|
+
|
|
39
|
+
return getPlayerProfileScoresFromHost(iteration, positions, connection, "https://api.impect.com")
|
|
40
|
+
|
|
41
|
+
def getPlayerProfileScoresFromHost(
|
|
42
|
+
iteration: int, positions: list, connection: RateLimitedAPI, host: str
|
|
43
|
+
) -> pd.DataFrame:
|
|
44
|
+
|
|
45
|
+
# check input for iteration argument
|
|
46
|
+
if not isinstance(iteration, int):
|
|
47
|
+
raise Exception("Input for iteration argument must be an integer")
|
|
48
|
+
|
|
49
|
+
# check input for positions argument
|
|
50
|
+
if not isinstance(positions, list):
|
|
51
|
+
raise Exception("Input for positions argument must be a list")
|
|
52
|
+
|
|
53
|
+
# check if the input positions are valid
|
|
54
|
+
invalid_positions = [position for position in positions if position not in allowed_positions]
|
|
55
|
+
if len(invalid_positions) > 0:
|
|
56
|
+
raise Exception(
|
|
57
|
+
f"Invalid position(s): {', '.join(invalid_positions)}."
|
|
58
|
+
f"\nChoose one or more of: {', '.join(allowed_positions)}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# get squads
|
|
62
|
+
squads = connection.make_api_request_limited(
|
|
63
|
+
url=f"{host}/v5/customerapi/iterations/{iteration}/squads",
|
|
64
|
+
method="GET"
|
|
65
|
+
).process_response(
|
|
66
|
+
endpoint="Squads"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# get squadIds
|
|
70
|
+
squad_ids = squads[squads.access].id.to_list()
|
|
71
|
+
|
|
72
|
+
# compile position string
|
|
73
|
+
position_string = ",".join(positions)
|
|
74
|
+
|
|
75
|
+
# get player profile scores per squad
|
|
76
|
+
profile_scores_raw = pd.concat(
|
|
77
|
+
map(lambda squadId: connection.make_api_request_limited(
|
|
78
|
+
url=f"{host}/v5/customerapi/iterations/{iteration}/"
|
|
79
|
+
f"squads/{squadId}/positions/{position_string}/player-profile-scores",
|
|
80
|
+
method="GET"
|
|
81
|
+
).process_response(
|
|
82
|
+
endpoint="PlayerIterationScores",
|
|
83
|
+
raise_exception=False
|
|
84
|
+
).assign(
|
|
85
|
+
iterationId=iteration,
|
|
86
|
+
squadId=squadId,
|
|
87
|
+
positions=position_string
|
|
88
|
+
),
|
|
89
|
+
squad_ids),
|
|
90
|
+
ignore_index=True)
|
|
91
|
+
|
|
92
|
+
# raise exception if no player played at given positions in entire iteration
|
|
93
|
+
if len(profile_scores_raw) == 0:
|
|
94
|
+
raise Exception(f"No players played at given position in iteration {iteration}.")
|
|
95
|
+
|
|
96
|
+
# print squads without players at given position
|
|
97
|
+
error_list = [str(squadId) for squadId in squad_ids if squadId not in profile_scores_raw.squadId.to_list()]
|
|
98
|
+
if len(error_list) > 0:
|
|
99
|
+
print(f"No players played at positions {positions} for iteration {iteration} for following squads:\n\t{', '.join(error_list)}")
|
|
100
|
+
|
|
101
|
+
# get players
|
|
102
|
+
players = connection.make_api_request_limited(
|
|
103
|
+
url=f"{host}/v5/customerapi/iterations/{iteration}/players",
|
|
104
|
+
method="GET"
|
|
105
|
+
).process_response(
|
|
106
|
+
endpoint="Players"
|
|
107
|
+
)[["id", "commonname", "firstname", "lastname", "birthdate", "birthplace", "leg", "countryIds", "idMappings"]]
|
|
108
|
+
|
|
109
|
+
# only keep first country id for each player
|
|
110
|
+
country_series = players["countryIds"].explode().groupby(level=0).first()
|
|
111
|
+
players["countryIds"] = players.index.to_series().map(country_series).astype("float").astype("Int64")
|
|
112
|
+
players = players.rename(columns={"countryIds": "countryId"})
|
|
113
|
+
|
|
114
|
+
# unnest mappings
|
|
115
|
+
players = unnest_mappings_df(players, "idMappings").drop(["idMappings"], axis=1).drop_duplicates()
|
|
116
|
+
|
|
117
|
+
# get scores
|
|
118
|
+
scores = connection.make_api_request_limited(
|
|
119
|
+
url=f"{host}/v5/customerapi/player-profiles",
|
|
120
|
+
method="GET"
|
|
121
|
+
).process_response(
|
|
122
|
+
endpoint="playerProfiles"
|
|
123
|
+
)[["name"]]
|
|
124
|
+
|
|
125
|
+
# get iterations
|
|
126
|
+
iterations = getIterationsFromHost(connection=connection, host=host)
|
|
127
|
+
|
|
128
|
+
# get country data
|
|
129
|
+
countries = connection.make_api_request_limited(
|
|
130
|
+
url=f"{host}/v5/customerapi/countries",
|
|
131
|
+
method="GET"
|
|
132
|
+
).process_response(
|
|
133
|
+
endpoint="KPIs"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# unnest scorings
|
|
137
|
+
profile_scores = profile_scores_raw.explode("profileScores").reset_index(drop=True)
|
|
138
|
+
|
|
139
|
+
# unnest dictionary in kpis column
|
|
140
|
+
profile_scores = pd.concat(
|
|
141
|
+
[profile_scores.drop(["profileScores"], axis=1), pd.json_normalize(profile_scores["profileScores"])],
|
|
142
|
+
axis=1
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# merge with player scores to ensure all kpis are present
|
|
146
|
+
profile_scores = profile_scores.merge(
|
|
147
|
+
scores,
|
|
148
|
+
left_on="profileName",
|
|
149
|
+
right_on="name",
|
|
150
|
+
how="outer",
|
|
151
|
+
suffixes=("", "_right")
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# get matchShares
|
|
155
|
+
match_shares = profile_scores[
|
|
156
|
+
["iterationId", "squadId", "playerId", "positions", "playDuration", "matchShare"]].drop_duplicates()
|
|
157
|
+
|
|
158
|
+
# fill missing values in the "name" column with a default value to ensure players without scorings don't get lost
|
|
159
|
+
if len(profile_scores["name"][profile_scores["name"].isnull()]) > 0:
|
|
160
|
+
profile_scores["name"] = profile_scores["name"].fillna("-1")
|
|
161
|
+
|
|
162
|
+
# pivot kpi values
|
|
163
|
+
profile_scores = pd.pivot_table(
|
|
164
|
+
profile_scores,
|
|
165
|
+
values="value",
|
|
166
|
+
index=["iterationId", "squadId", "playerId", "positions"],
|
|
167
|
+
columns="name",
|
|
168
|
+
aggfunc="sum",
|
|
169
|
+
fill_value=0,
|
|
170
|
+
dropna=False
|
|
171
|
+
).reset_index()
|
|
172
|
+
|
|
173
|
+
# drop "-1" column
|
|
174
|
+
if "-1" in profile_scores.columns:
|
|
175
|
+
profile_scores.drop(["-1"], inplace=True, axis=1)
|
|
176
|
+
|
|
177
|
+
# merge with playDuration and matchShare
|
|
178
|
+
profile_scores = profile_scores.merge(
|
|
179
|
+
match_shares,
|
|
180
|
+
left_on=["iterationId", "squadId", "playerId", "positions"],
|
|
181
|
+
right_on=["iterationId", "squadId", "playerId", "positions"],
|
|
182
|
+
how="inner",
|
|
183
|
+
suffixes=("", "_right")
|
|
184
|
+
)
|
|
185
|
+
# merge with other data
|
|
186
|
+
profile_scores = profile_scores.merge(
|
|
187
|
+
iterations[["id", "competitionName", "season"]],
|
|
188
|
+
left_on="iterationId",
|
|
189
|
+
right_on="id",
|
|
190
|
+
how="left",
|
|
191
|
+
suffixes=("", "_right")
|
|
192
|
+
).merge(
|
|
193
|
+
squads[["id", "name"]].rename(
|
|
194
|
+
columns={"id": "squadId", "name": "squadName"}
|
|
195
|
+
),
|
|
196
|
+
left_on="squadId",
|
|
197
|
+
right_on="squadId",
|
|
198
|
+
how="left",
|
|
199
|
+
suffixes=("", "_right")
|
|
200
|
+
).merge(
|
|
201
|
+
players[[
|
|
202
|
+
"id", "wyscoutId", "heimSpielId", "skillCornerId", "commonname",
|
|
203
|
+
"firstname", "lastname", "birthdate", "birthplace", "countryId", "leg"
|
|
204
|
+
]].rename(
|
|
205
|
+
columns={"commonname": "playerName"}
|
|
206
|
+
),
|
|
207
|
+
left_on="playerId",
|
|
208
|
+
right_on="id",
|
|
209
|
+
how="left",
|
|
210
|
+
suffixes=("", "_right")
|
|
211
|
+
).merge(
|
|
212
|
+
countries.rename(columns={"fifaName": "playerCountry"}),
|
|
213
|
+
left_on="countryId",
|
|
214
|
+
right_on="id",
|
|
215
|
+
how="left",
|
|
216
|
+
suffixes=("", "_right")
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# remove NA rows
|
|
220
|
+
profile_scores = profile_scores[profile_scores.iterationId.notnull()]
|
|
221
|
+
|
|
222
|
+
# fix column types
|
|
223
|
+
profile_scores["squadId"] = profile_scores["squadId"].astype("Int64")
|
|
224
|
+
profile_scores["playerId"] = profile_scores["playerId"].astype("Int64")
|
|
225
|
+
profile_scores["iterationId"] = profile_scores["iterationId"].astype("Int64")
|
|
226
|
+
profile_scores["wyscoutId"] = profile_scores["wyscoutId"].astype("Int64")
|
|
227
|
+
profile_scores["heimSpielId"] = profile_scores["heimSpielId"].astype("Int64")
|
|
228
|
+
profile_scores["skillCornerId"] = profile_scores["skillCornerId"].astype("Int64")
|
|
229
|
+
|
|
230
|
+
# define column order
|
|
231
|
+
order = [
|
|
232
|
+
"iterationId",
|
|
233
|
+
"competitionName",
|
|
234
|
+
"season",
|
|
235
|
+
"squadId",
|
|
236
|
+
"squadName",
|
|
237
|
+
"playerId",
|
|
238
|
+
"wyscoutId",
|
|
239
|
+
"heimSpielId",
|
|
240
|
+
"skillCornerId",
|
|
241
|
+
"playerName",
|
|
242
|
+
"firstname",
|
|
243
|
+
"lastname",
|
|
244
|
+
"birthdate",
|
|
245
|
+
"birthplace",
|
|
246
|
+
"playerCountry",
|
|
247
|
+
"leg",
|
|
248
|
+
"positions",
|
|
249
|
+
"matchShare",
|
|
250
|
+
"playDuration"
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
# add kpiNames to order
|
|
254
|
+
order = order + scores.name.to_list()
|
|
255
|
+
|
|
256
|
+
# select columns
|
|
257
|
+
profile_scores = profile_scores[order]
|
|
258
|
+
|
|
259
|
+
# return result
|
|
260
|
+
return profile_scores
|