Skip to content
Merged
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
7 changes: 6 additions & 1 deletion autonomy/sports/espn.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,12 @@ def _probable_era(competitor: dict[str, Any]) -> tuple[float | None, str | None]
if not probs:
return None, None
p = probs[0]
name = p.get("displayName") or (p.get("athlete") or {}).get("displayName")
# The probables entry carries BOTH a slot label and the athlete. Its own
# ``displayName`` is the label -- literally "Probable Starting Pitcher" --
# so reading it first meant the athlete fallback never ran and every game
# recorded the same constant string as its starter. Ask the athlete first.
athlete = p.get("athlete") or {}
name = athlete.get("displayName") or athlete.get("fullName") or p.get("displayName")
for stat in p.get("statistics") or []:
if str(stat.get("name", "")).lower() in ("era", "earnedrunaverage"):
try:
Expand Down
38 changes: 38 additions & 0 deletions tests/test_espn_lake_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,41 @@ def test_probable_pitcher_fields_are_omitted_not_null_when_absent(tmp_path):
(row,) = espn_games_to_rows([game], source="espn", received_at="2026-08-12T18:00:00Z")
assert "home_probable_pitcher" not in row["extra"]
assert "away_probable_pitcher" not in row["extra"]


def test_probable_starter_is_the_athlete_not_the_slot_label():
"""ESPN's probables entry carries a label AND an athlete; take the athlete.

The entry's own ``displayName`` is the literal string "Probable Starting
Pitcher". Reading it first meant the athlete fallback never ran and every
game in the lake recorded the same constant as its starter -- ERAs varied
correctly, so the rows looked populated while pitcher identity, the whole
point of the field, was uniform garbage. The assertion is on precedence,
because the label is always present.
"""

from autonomy.sports.espn import _probable_era

era, name = _probable_era(
{
"probables": [
{
"name": "probableStartingPitcher",
"displayName": "Probable Starting Pitcher",
"athlete": {"displayName": "Jameson Taillon", "fullName": "Jameson Taillon"},
"statistics": [{"name": "ERA", "displayValue": "2.87"}],
}
]
}
)
assert name == "Jameson Taillon"
assert era == 2.87


def test_probable_starter_falls_back_to_the_label_only_when_no_athlete():
from autonomy.sports.espn import _probable_era

_, name = _probable_era(
{"probables": [{"displayName": "Probable Starting Pitcher", "statistics": []}]}
)
assert name == "Probable Starting Pitcher"
Loading