fix: address PR review — correct Peewee DISTINCT syntax and Decision-only pitchers

- fn.COUNT(fn.DISTINCT(expr)) → fn.COUNT(expr.distinct()) for correct
  COUNT(DISTINCT ...) SQL on PostgreSQL
- _get_player_pairs() now also scans Decision table to include pitchers
  who have a Decision row but no StratPlay rows (rare edge case)
- Updated stale docstring references to PlayerSeasonStats and r.k

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-03-19 10:23:47 -05:00
parent 1b4eab9d99
commit 4211bd69e0
2 changed files with 21 additions and 9 deletions

View File

@ -3,7 +3,7 @@
Force-recalculates a card's evolution state from career totals.
evaluate_card() is the main entry point:
1. Load career totals: SUM all player_season_stats rows for (player_id, team_id)
1. Load career totals: SUM all BattingSeasonStats/PitchingSeasonStats rows for (player_id, team_id)
2. Determine track from card_state.track
3. Compute formula value (delegated to formula engine, WP-09)
4. Compare value to track thresholds to determine new_tier
@ -14,9 +14,9 @@ evaluate_card() is the main entry point:
Idempotent: calling multiple times with the same data produces the same result.
Depends on WP-05 (EvolutionCardState), WP-07 (PlayerSeasonStats), and WP-09
(formula engine). Models and formula functions are imported lazily so this
module can be imported before those PRs merge.
Depends on WP-05 (EvolutionCardState), WP-07 (BattingSeasonStats/PitchingSeasonStats),
and WP-09 (formula engine). Models and formula functions are imported lazily so
this module can be imported before those PRs merge.
"""
from datetime import datetime
@ -29,7 +29,7 @@ class _CareerTotals:
Passed to the formula engine as a stats-duck-type object with the attributes
required by compute_value_for_track:
batter: pa, hits, doubles, triples, hr
sp/rp: outs, k
sp/rp: outs, strikeouts
"""
__slots__ = ("pa", "hits", "doubles", "triples", "hr", "outs", "strikeouts")

View File

@ -48,8 +48,9 @@ def _get_player_pairs(game_id: int) -> tuple[set, set]:
Queries StratPlay for all rows belonging to game_id and extracts:
- batting_pairs: set of (batter_id, batter_team_id), excluding rows where
batter_id is None (e.g. automatic outs, walk-off plays without a PA).
- pitching_pairs: set of (pitcher_id, pitcher_team_id) for all plays
(pitcher is always present).
- pitching_pairs: set of (pitcher_id, pitcher_team_id) from all plays
(pitcher is always present), plus any pitchers from the Decision table
who may not have StratPlay rows (rare edge case).
Args:
game_id: Primary key of the StratGame to query.
@ -77,6 +78,17 @@ def _get_player_pairs(game_id: int) -> tuple[set, set]:
batting_pairs.add((batter_id, batter_team_id))
pitching_pairs.add((pitcher_id, pitcher_team_id))
# Include pitchers who have a Decision but no StratPlay rows for this game
# (rare edge case, e.g. a pitcher credited with a decision without recording
# any plays — the old code handled this explicitly in _apply_decisions).
decision_pitchers = (
Decision.select(Decision.pitcher, Decision.pitcher_team)
.where(Decision.game == game_id)
.tuples()
)
for pitcher_id, pitcher_team_id in decision_pitchers:
pitching_pairs.add((pitcher_id, pitcher_team_id))
return batting_pairs, pitching_pairs
@ -104,7 +116,7 @@ def _recalc_batting(player_id: int, team_id: int, season: int) -> dict:
row = (
StratPlay.select(
fn.COUNT(
fn.DISTINCT(Case(None, [(StratPlay.pa > 0, StratPlay.game)], None))
Case(None, [(StratPlay.pa > 0, StratPlay.game)], None).distinct()
).alias("games"),
fn.SUM(StratPlay.pa).alias("pa"),
fn.SUM(StratPlay.ab).alias("ab"),
@ -190,7 +202,7 @@ def _recalc_pitching(player_id: int, team_id: int, season: int) -> dict:
"""
row = (
StratPlay.select(
fn.COUNT(fn.DISTINCT(StratPlay.game)).alias("games"),
fn.COUNT(StratPlay.game.distinct()).alias("games"),
fn.SUM(StratPlay.outs).alias("outs"),
fn.SUM(StratPlay.so).alias("strikeouts"),
fn.SUM(StratPlay.hit).alias("hits_allowed"),