Skip to content

Commit c556c9a

Browse files
dwillisclaude
andcommitted
feat: add bowling_scorecard property with _bowling_entry helper
Implements bowling_scorecard (list[list[dict]]) and _bowling_entry helper on Match, mirroring the batting_scorecard pattern. Also tightens the batting_scorecard return-type annotation from `list` to `list[list[dict]]`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5eb6808 commit c556c9a

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

espncricinfo/match.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ def _batting_entry(self, raw: dict) -> dict:
716716
}
717717

718718
@property
719-
def batting_scorecard(self) -> list:
719+
def batting_scorecard(self) -> list[list[dict]]:
720720
"""
721721
Return batting scorecard for all innings as ``list[list[dict]]``.
722722
@@ -740,6 +740,49 @@ def batting_scorecard(self) -> list:
740740
result.append([])
741741
return result
742742

743+
def _bowling_entry(self, raw: dict) -> dict:
744+
"""Transform a raw inningBowlers dict into a clean flat dict."""
745+
player = raw.get("player") or {}
746+
economy = raw.get("economy")
747+
overs = raw.get("overs")
748+
return {
749+
"name": player.get("name"),
750+
"full_name": player.get("longName"),
751+
"player_id": player.get("objectId"),
752+
"overs": float(overs) if overs is not None else None,
753+
"maidens": raw.get("maidens"),
754+
"runs": raw.get("conceded"),
755+
"wickets": raw.get("wickets"),
756+
"economy": float(economy) if economy is not None else None,
757+
"wides": raw.get("wides"),
758+
"no_balls": raw.get("noballs"),
759+
"dots": raw.get("dots"),
760+
}
761+
762+
@property
763+
def bowling_scorecard(self) -> list[list[dict]]:
764+
"""
765+
Return bowling scorecard for all innings as ``list[list[dict]]``.
766+
767+
Outer list is indexed by innings order; inner list has one entry
768+
per bowler.
769+
770+
Example::
771+
772+
for i, innings in enumerate(m.bowling_scorecard, 1):
773+
print(f"Innings {i}:")
774+
for b in innings:
775+
print(f" {b['name']}: {b['wickets']}/{b['runs']} ({b['overs']} ov)")
776+
"""
777+
result = []
778+
for i in range(1, len(self.innings) + 1):
779+
raw_list = self.bowlers(i)
780+
if raw_list:
781+
result.append([self._bowling_entry(r) for r in raw_list])
782+
else:
783+
result.append([])
784+
return result
785+
743786
# ------------------------------------------------------------------
744787
# Static helpers
745788
# ------------------------------------------------------------------

tests/test_match.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,44 @@ def patched_batsmen(i):
351351
with patch.object(self.match, 'batsmen', side_effect=patched_batsmen):
352352
sc = self.match.batting_scorecard
353353
self.assertEqual(sc[1], [])
354+
355+
# ---- bowling_scorecard ----
356+
357+
def test_bowling_scorecard_is_list_of_2_innings(self):
358+
sc = self.match.bowling_scorecard
359+
self.assertIsInstance(sc, list)
360+
self.assertEqual(len(sc), 2)
361+
362+
def test_bowling_scorecard_innings_are_nonempty_lists(self):
363+
for innings in self.match.bowling_scorecard:
364+
self.assertIsInstance(innings, list)
365+
self.assertGreater(len(innings), 0)
366+
367+
def test_bowling_scorecard_entry_has_required_keys(self):
368+
entry = self.match.bowling_scorecard[0][0]
369+
for key in ("name", "full_name", "player_id", "overs", "maidens",
370+
"runs", "wickets", "economy", "wides", "no_balls", "dots"):
371+
self.assertIn(key, entry)
372+
373+
def test_bowling_scorecard_first_bowler_values(self):
374+
# Darcie Brown bowled first innings
375+
entry = self.match.bowling_scorecard[0][0]
376+
self.assertEqual(entry["name"], "D Brown")
377+
self.assertIsInstance(entry["wickets"], int)
378+
self.assertIsInstance(entry["overs"], float)
379+
380+
def test_bowling_scorecard_economy_is_float_or_none(self):
381+
entry = self.match.bowling_scorecard[0][0]
382+
self.assertTrue(
383+
entry["economy"] is None or isinstance(entry["economy"], float)
384+
)
385+
386+
def test_bowling_scorecard_empty_innings_returns_empty_list(self):
387+
original_bowlers = self.match.bowlers
388+
def patched_bowlers(i):
389+
if i == 2:
390+
return None
391+
return original_bowlers(i)
392+
with patch.object(self.match, 'bowlers', side_effect=patched_bowlers):
393+
sc = self.match.bowling_scorecard
394+
self.assertEqual(sc[1], [])

0 commit comments

Comments
 (0)