From 4211bd69e052fa9ccd9cf13e2625a9e486e132f0 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 19 Mar 2026 10:23:47 -0500 Subject: [PATCH] =?UTF-8?q?fix:=20address=20PR=20review=20=E2=80=94=20corr?= =?UTF-8?q?ect=20Peewee=20DISTINCT=20syntax=20and=20Decision-only=20pitche?= =?UTF-8?q?rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- app/services/evolution_evaluator.py | 10 +++++----- app/services/season_stats.py | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/services/evolution_evaluator.py b/app/services/evolution_evaluator.py index 81dab5a..1b2c033 100644 --- a/app/services/evolution_evaluator.py +++ b/app/services/evolution_evaluator.py @@ -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") diff --git a/app/services/season_stats.py b/app/services/season_stats.py index ba82e8f..991bfa5 100644 --- a/app/services/season_stats.py +++ b/app/services/season_stats.py @@ -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"),