Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion airsenal/framework/data_fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,9 @@ def get_num_free_transfers(self, fpl_team_id=None):
Requires login
"""
squad_data = self.get_current_squad_data(fpl_team_id)
return squad_data["transfers"]["limit"]
return max(
0, squad_data["transfers"]["limit"] - squad_data["transfers"]["made"]
)

def get_current_bank(self, fpl_team_id=None):
"""
Expand Down
2 changes: 1 addition & 1 deletion airsenal/framework/optimization_squad.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def make_new_squad(
player = opt_squad.players[int(idx)]
print(
player.position(season),
player.name,
player,
player.team(season, 1),
player.price(season, 1) / 10,
)
Expand Down
4 changes: 2 additions & 2 deletions airsenal/framework/optimization_transfers.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def make_optimum_single_transfer(
best_squad = new_squad
break
if verbose:
print(f"Failed to add {p_in[0].name}")
print(f"Failed to add {p_in[0]}")
if not new_squad.is_complete() and verbose:
print(f"Failed to find a valid replacement for {p_out.player_id}")

Expand Down Expand Up @@ -281,7 +281,7 @@ def make_random_transfers(
ap.player_id, gameweek=transfer_gw
)
if not removed_ok:
print(f"Problem removing {ap.name}")
print(f"Problem removing {ap}")
added_players = []

# calculate the score
Expand Down
21 changes: 12 additions & 9 deletions airsenal/framework/optimization_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ def next_week_transfers(
"""Given a previous strategy and some optimisation constraints, determine the valid
options for the number of transfers (or chip played) in the following gameweek.

strat is a tuple (free_transfers, hit_so_far, strat_dict)
strat is a tuple (free_transfers, total_points_hit, strat_dict)
strat_dict must have key chips_played, which is a dict indexed by gameweek with
possible values None, "wildcard", "free_hit", "bench_boost" or triple_captain"

Expand All @@ -446,7 +446,9 @@ def next_week_transfers(
max_free_transfers - maximum number of free transfers saved in the game rules
(2 before 2024/25, 5 from 2024/25 season)

Returns (new_transfers, new_ft_available, new_points_hits) tuples.
Returns (new_transfers, new_ft_available, total_points_hit, hit_this_gw) tuples.
- total_points_hit is the total points hit so far including this gw
- hit_this_gw is the points hit incurred this gameweek
"""
# check that the 'chips' dict we are given makes sense:
if chips is None:
Expand Down Expand Up @@ -524,17 +526,18 @@ def next_week_transfers(
if allow_triple_captain:
new_transfers += [f"T{nt}" for nt in ft_choices]

new_points_hits = [
hit_so_far + calc_points_hit(nt, ft_available) for nt in new_transfers
]
hit_this_gw = [calc_points_hit(nt, ft_available) for nt in new_transfers]
total_points_hit = [hit_so_far + hit for hit in hit_this_gw]
new_ft_available = [
calc_free_transfers(nt, ft_available, max_free_transfers)
for nt in new_transfers
]

# return list of (num_transfers, free_transfers, hit_so_far) tuples for each new
# strategy
return list(zip(new_transfers, new_ft_available, new_points_hits, strict=False))
# return list of (num_transfers, free_transfers, total_points_hit, hit_this_gw)
# tuples for each new strategy
return list(
zip(new_transfers, new_ft_available, total_points_hit, hit_this_gw, strict=True)
)


def count_expected_outputs(
Expand Down Expand Up @@ -590,7 +593,7 @@ def count_expected_outputs(
max_free_transfers=max_free_transfers,
)

for n_transfers, new_free_transfers, new_hit in possibilities:
for n_transfers, new_free_transfers, new_hit, _ in possibilities:
# make a copy of the strategy up to this point, then add on this gw
new_dict = deepcopy(s[2])

Expand Down
6 changes: 4 additions & 2 deletions airsenal/framework/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(
pdata = p
self.player_id = pdata.player_id
self.name = pdata.name
self.display_name = pdata.display_name
self.season = season
team = pdata.team(season, gameweek)
if team is None:
Expand All @@ -64,7 +65,7 @@ def __init__(
self.sub_position = None

def __str__(self):
return self.name
return self.display_name or self.name

def calc_predicted_points(self, tag):
"""
Expand All @@ -83,7 +84,7 @@ def get_predicted_points(self, gameweek, tag):
if tag not in self.predicted_points:
self.calc_predicted_points(tag)
if gameweek not in self.predicted_points[tag]:
print(f"No prediction available for {self.name} week {gameweek}")
print(f"No prediction available for {self} week {gameweek}")
return 0.0
return self.predicted_points[tag][gameweek]

Expand All @@ -95,6 +96,7 @@ class DummyPlayer:

def __init__(self, gw_range, tag, position, purchase_price=45, pts=0):
self.name = "DUMMY"
self.display_name = "DUMMY"
self.position = position
self.purchase_price = purchase_price
# set team to random string so we don't violate max players per team constraint
Expand Down
9 changes: 5 additions & 4 deletions airsenal/framework/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Player(Base):
player_id: Mapped[intpk] = mapped_column(autoincrement=True)
fpl_api_id: Mapped[int | None]
name: Mapped[str100]
display_name: Mapped[str100 | None]
opta_code: Mapped[str | None]
attributes: Mapped[list["PlayerAttributes"]] = relationship(back_populates="player")
absences: Mapped[list["Absence"]] = relationship(back_populates="player")
Expand All @@ -60,7 +61,7 @@ def team(self, season: str, gameweek: int) -> str | None:
attr = self.get_gameweek_attributes(season, gameweek)
if attr is not None and not isinstance(attr, tuple):
return attr.team
print("No team found for", self.name, "in", season, "season.")
print(f"No team found for {self} in {season} season.")
return None

def price(self, season: str, gameweek: int) -> int | None:
Expand All @@ -73,7 +74,7 @@ def price(self, season: str, gameweek: int) -> int | None:
attr = self.get_gameweek_attributes(season, gameweek, before_and_after=True)
if attr is not None:
return self._calculate_price(attr, gameweek)
print("No price found for", self.name, "in", season, "season.")
print(f"No price found for {self} in {season} season.")
return None

def _calculate_price(
Expand Down Expand Up @@ -105,7 +106,7 @@ def position(self, season: str) -> str | None:
attr = self.get_gameweek_attributes(season, None)
if attr is not None and not isinstance(attr, tuple):
return attr.position
print("No position found for", self.name, "in", season, "season.")
print(f"No position found for {self} in {season} season.")
return None

def is_injured_or_suspended(
Expand Down Expand Up @@ -175,7 +176,7 @@ def get_gameweek_attributes(
return attr_after

def __str__(self):
return self.name
return self.display_name or self.name


class PlayerMapping(Base):
Expand Down
12 changes: 7 additions & 5 deletions airsenal/framework/squad.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,22 @@ def __repr__(self):
"""
Display the squad
"""
print("\n=== starting 11 ===\n")
for position in ["GK", "DEF", "MID", "FWD"]:
print(f"\n== {position} ==\n")
for p in self.players:
if p.position == position and p.is_starting:
player_line = f"{p.name} ({p.team})"
player_line = f"{p} ({p.team})"
if p.is_captain:
player_line += "(C)"
elif p.is_vice_captain:
player_line += "(VC)"
print(player_line)
print("\n=== subs ===\n")
print("\n=== Subs ===\n")

subs = [p for p in self.players if not p.is_starting]
subs.sort(key=lambda p: p.sub_position)
for p in subs:
print(f"{p.name} ({p.team})")
print(f"{p} ({p.team})")
return ""

def is_complete(self):
Expand Down Expand Up @@ -111,6 +110,10 @@ def add_player(
player.season = self.season
if price is not None:
player.purchase_price = price

if self.verbose:
print(f"Adding player {p}")

if player.position == "MNG":
warnings.warn(
f"Skipped adding manager {player}, assistant manager not implemented."
Expand Down Expand Up @@ -540,7 +543,6 @@ def get_current_squad_from_api(
player = get_player_from_api_id(p["element"])
if not player:
continue
print(f"Adding player {player}")
squad.add_player(
player,
price=p["purchase_price"],
Expand Down
14 changes: 6 additions & 8 deletions airsenal/framework/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ def get_player_name(player_id: int, dbsession: Session = session) -> str | None:
Lookup player name, for human readability.
"""
if p := get_player(player_id, dbsession):
return p.name
return str(p)
print(f"Unknown player_id {player_id}")
return None

Expand Down Expand Up @@ -709,7 +709,7 @@ def list_players(
)
if verbose:
for pa in sort_players:
print(pa[1].name, pa[0])
print(pa[1], pa[0])
players = [p for _, p in sort_players]
return players

Expand Down Expand Up @@ -1037,9 +1037,7 @@ def get_predicted_points_for_player(
# for double gameweeks, we need to add the two together
gameweek = prediction.fixture.gameweek
if gameweek is None:
print(
f"Player {player.name} has no gameweek for fixture {prediction.fixture}"
)
print(f"Player {player} has no gameweek for fixture {prediction.fixture}")
continue
if gameweek not in ppdict:
ppdict[gameweek] = 0.0
Expand Down Expand Up @@ -1165,7 +1163,7 @@ def get_top_predicted_points(
price = p[0].price(season, first_gw)
price_str = str(price / 10) if price is not None else "UNKNOWN_PRICE"
print(
f"{i + 1}. {p[0].name}, {p[1]:.2f}pts "
f"{i + 1}. {p[0]}, {p[1]:.2f}pts "
f"(£{price_str}m, {p[0].position(season)}, "
f"{p[0].team(season, first_gw)})"
)
Expand Down Expand Up @@ -1224,7 +1222,7 @@ def get_top_predicted_points(
else "UNKNOWN_PRICE"
)
print(
f"{i + 1}. {p[0].name}, {p[1]:.2f}pts "
f"{i + 1}. {p[0]}, {p[1]:.2f}pts "
f"(£{price_str}m, "
f"{p[0].team(season, first_gw)})"
)
Expand Down Expand Up @@ -1286,7 +1284,7 @@ def predicted_points_discord_payload(
[
{
"name": "Player",
"value": f"{i + 1}. {p[0].name}",
"value": f"{i + 1}. {p[0]}",
"inline": True,
},
{
Expand Down
4 changes: 2 additions & 2 deletions airsenal/scripts/fill_player_attributes_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def fill_attributes_table_from_api(
print(f"ATTRIBUTES {season} No player found with id {player_api_id}")
continue

print(f"ATTRIBUTES {season} {player.name}")
print(f"ATTRIBUTES {season} {player}")

# First update the current gameweek using the summary data
p_summary = input_data[player_api_id]
Expand Down Expand Up @@ -167,7 +167,7 @@ def fill_attributes_table_from_api(
if next_gw > 1:
player_data = fetcher.get_gameweek_data_for_player(player_api_id)
if not player_data:
print("Failed to get data for", player.name)
print(f"Failed to get data for {player}")
continue
for gameweek, data in player_data.items():
if gameweek < gw_start:
Expand Down
2 changes: 2 additions & 0 deletions airsenal/scripts/fill_player_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,11 @@ def fill_player_table_from_api(season: str, dbsession: Session) -> None:
first_name = v["first_name"] # .encode("utf-8")
second_name = v["second_name"] # .encode("utf-8")
name = f"{first_name} {second_name}"
display_name = v.get("web_name")

print(f"PLAYER {season} {name}")
p.name = name
p.display_name = display_name
p.opta_code = v["opta_code"]
dbsession.add(p)
dbsession.commit()
Expand Down
Loading