Spaces:
Sleeping
Sleeping
Commit History
-
Upgrade Game Summary to Kasper HUD poster design
Replaces flat card_renderer output with full Kasper HUD visual language: dark star background, cyan glow borders, HUD corners, score banner with amber scores, hitter table with alternating rows, stat strip, and footer. New functions in poster_renderer.py: - render_game_summary_poster() — public entry point - _draw_score_banner() — team names + scores + date panel - _draw_game_hitter_table_poster() — TOP HITTERS table with glow row dividers card_lab_page.py: swaps import + _gen_game_bytes() call to new poster renderer. card_renderer.render_game_summary_card() preserved for rollback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8a468b1
-
Add live 2026 pitch/PA tables and migrate Card Lab to new sources
New tables live_pitch_mix_2026 (pitch-level) and live_batter_game_log_2026 (PA-level) are created in db.py and wired into the post-game ingestion pipeline in game_completion.py. Card Lab queries are year-gated: 2026 hitter/pitcher cards and all Game Summary cards now read from the new live tables (no 3-table JOIN), while 2021-2025 cards continue using statcast_event_core. Game Summary hitter rows are now sourced from live_batter_game_log_2026 (one row per PA, real batter identity via MLBAM ID) instead of grouping pitch-level data by pitcher name. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9846940 -
Card Lab Game Summary: fix empty selectors via statcast coverage filter
Root cause: game_outcomes contains 2026/recent games with no matching rows in statcast_event_core (historical data ends at 2025). Game pk lookups returned 0 rows silently, leaving multiselects empty. - get_recent_completed_games_for_card_lab: restrict dropdown to games whose game_pk exists in statcast_event_core (CAST bigint AS STRING for safe text comparison against game_outcomes.game_pk text column) - get_game_players_for_card_lab: surface exceptions via _error key in result dict instead of swallowing silently - Debug expander: show raw game_pk type, statcast row/pitcher/batter counts for selected game, and any helper exception text Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>b00ec4c -
Card Lab Game Summary: per-game hitter + pitcher multiselects
- get_game_players_for_card_lab(): queries pitchers from ec.player_name (Last,First converted to First Last) and batters from ec.batter MLBAM IDs resolved via player_identity_map. Team labels inferred from inning_topbot. - build_game_summary_card_data(): add selected_hitters / selected_pitchers params (optional, backward-compatible); plumbed into payload for Phase 2 renderer use. - card_lab_page.py: replace single pitcher selectbox with hitter + pitcher multiselects ("TEAM — Player Name" labels); selections wired through to generation payload; debug expander shows game_pk, player counts, first 10 each. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>573fe1e -
Card Lab Pass 5: identity map + batter_id-based hitter query
- Build player_identity_map.parquet from pybaseball + MLBAM lookup + DB enrichment - Pitcher statcast_name resolved from ec.player_name (Last, First format) - Hitter statcast_name set to canonical_name; hitter cards now query by ec.batter = :batter_id - card_lab_pitcher_seasons.parquet and card_lab_hitter_seasons.parquet built from pybaseball pitching_stats/batting_stats with IP>0/AB>0 gates - get_player_card_window_df: add batter_id param for Hitter path (ec.batter = :batter_id) - card_lab_page.py: read batter_id from Parquet, pass to window query; no DB call for MLBAM ID - Collision-safe display_names; all validation checks pass (ALL PASS) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>84163a9 -
Card Lab Pass 5: player identity layer + pybaseball season-summary selectors
Replaces event-table selectors (bze/pife) with materialized pybaseball-derived season summaries. Adds canonical player_identity_map with normalized matching, MLBAM ID resolution, collision-safe display names, and verified statcast_name enrichment so hitter downstream queries are guaranteed to match. - visualization/cards/player_identity.py: new identity layer (normalize_for_matching, to_canonical_name, get_hitter_selector_data, get_pitcher_selector_data) - card_queries.py: selector functions now read card_lab_*_seasons.parquet; IP>0/AB>0 workload gates; backward-compat constants kept - card_lab_page.py: hitter branch uses verified statcast_name (hard fail if missing, no fallback); pitcher branch uses player_id from parquet; full debug expander - scripts/build_player_identity_map.py: offline build with DB statcast enrichment - scripts/build_card_lab_season_summaries.py: joins pybaseball through identity map - scripts/validate_card_lab_build.py: acceptance gate before committing a…8f839e6 -
Card Lab Pass 4: role-specific selector source rebuild
Replace statcast_event_core-based selectors with dedicated role-specific tables. card_queries.py: - Delete get_card_lab_hitters() and get_card_lab_pitchers() (statcast_event_core source) - Add get_pitcher_selector_names(): sources from pitcher_inning_first_seed_events (pitcher-only table, no role contamination, game_date year filter, COUNT >= 10) - Add get_hitter_selector_names(): sources from batter_zone_events (hitter-only table, no role contamination, game_date year filter, COUNT >= 20) - Add get_pitcher_id_by_name(): exact raw name lookup against pife for pitcher_id - Add _PITCHER_SELECTOR_MIN_EVENTS / _HITTER_SELECTOR_MIN_EVENTS constants card_lab_page.py: - Use new role-specific selectors; remove global_name_map / fetch_mlb_player_name_map - Remove overlap validation (no longer needed — sources are disjoint by design) - Both branches use display_to_raw mapping: normalize for display, raw for SQL queries - Debug expander shows source table, year filter, workload filter, spot…86686b5 -
Card Lab Pass 3: deterministic ID-based selector rebuild
card_queries.py: - Add normalize_name() (was page-only; now shared with get_card_lab_pitchers) - Rewrite get_card_lab_pitchers(): 2-JOIN SQL (pife + batter cross-ref api_name), pitcher_map dedup by ID, counter-based collision handling for 3+ shared names, returns list[tuple[int,str]] — no meta dict - Rewrite get_card_lab_hitters(): remove LIMIT, unified param dict, returns raw DB names (callers normalize for display) card_lab_page.py: - Delete all @st.cache_data wrappers; call functions directly every render - Import fetch_mlb_player_name_map/resolve_player_image at module level - Fetch global_name_map once before both branches (fixes overlap validation scope) - Hitter branch: hitter_display_to_raw mapping preserves raw DB name for SQL queries - Pitcher branch: assert no duplicate IDs; plain dict comprehension (no redundant dedup) - Overlap validation: explicit dual fetch of both selector sets every render; st.error + logger + RuntimeError(debug_mode) on any overlap - Auto-ima…3dcb68c
-
Card Lab: hard pitcher exclusion + dedupe + normalized plain-string selectors
Fixes three confirmed bugs: 1. Duplicate Paul Skenes: pitcher_inning_first_seed_events had one row per seeded game event; LEFT JOIN multiplied output rows. Fixed by pre-aggregating pife with GROUP BY pitcher_id in both SQL variants of get_card_lab_pitchers(). Python dedup by seen_ids set added as belt-and-suspenders. 2. Freddy Peralta / pitchers in hitter list: old batter_count > pitcher_count dominance filter failed when spring-training batter appearances inflated pitcher batter_count while pitcher rows landed in a different source_season. Replaced with hard pitcher exclusion set: any player who threw >= 100 pitches as pitcher is unconditionally excluded from hitter selector. Diagnostic logging added to verify Peralta/Skubal/Cole/Skenes are in the exclusion set. 3. Substring search broken: format_func on tuple options may not propagate to Streamlit selectbox search in v1.39. Both hitter and pitcher selectors now use pre-normalized plain "First Last" st…2c19812 -
Card Lab: fix pitcher selector + internal image system + remove upload
- get_card_lab_pitchers(): replace sparse pife-only JOIN with COALESCE batter cross-ref subquery so DH-era pitchers resolve via shared MLBAM ID namespace; MLB Stats API name_map fills any remaining unknowns; returns (pitchers, meta) tuple with db/api/unknown resolution counts - get_batter_mlbam_id(): new helper for hitter MLBAM ID lookup by name - player_assets.py (new): 3-layer image resolver (Kasper local asset → MLB headshot fallback → None/silhouette); fetch_mlb_player_name_map() bulk-fetches season roster from public MLB Stats API - card_lab_page.py: remove file uploader entirely; wire auto-image via _cached_player_image(mlbam_id); add Debug expander with pitcher count, source, db/api/unknown counts, first 20 names, spot checks for Skubal/ Gray/Liberatore/Leahy/Sheehan; re-enable hitter cache @st.cache_data; apply name_map post-processing after cached pitcher SQL query - poster_renderer.py: change "UPLOAD PLAYER PHOTO" placeholder to "NO IMAGE" - assets/players/:…6735616 -
Card Lab: Kasper Report poster renderer (1024×1536 HUD template)
Removes page lock. Introduces template-based poster system: poster_templates.py - THEMES dict: kasper_hud color palette (bg, panels, cyan/amber/red accents) - TEMPLATES dict: kasper_report layout with named zones (x,y,w,h) poster_renderer.py - build_background(): procedural dark navy + noise grain + star particles + vignette - process_player_image(): rembg bg removal (graceful fallback) → rim light → glow halo - _glow_rect() / _glow_line(): blurred glow layers composited under crisp borders - _hud_corners(): L-bracket corner decorations - Zone drawers: header, metric boxes, chart panel, stat row, readout, alert, rolling, footer - _apply_final_effects(): radial vignette post-pass - render_hitter_poster() / render_pitcher_poster(): full pipeline entry points card_lab_page.py - Player photo upload (optional PNG/JPG, rembg removes bg automatically) - Hitter/Pitcher now call poster renderer; Game Summary retains classic card renderer - Signature updated: _gen_hitt…edd5c8e -
Hitter selector: ID-based role dominance classification
Replaces name-based exclusion with batter/pitcher numeric ID comparison. Two queries against statcast_event_core: 1. batter id -> (player_name, batter_count) via GROUP BY batter 2. pitcher id -> pitcher_count via GROUP BY pitcher Eligibility rule: batter_count >= 20 AND batter_count > pitcher_count. Display names come from player_name (the batter name column in the table). No name-matching logic used for eligibility decisions. Diagnostic logging preserved: logs Ivan Herrera / Mick Abel / Jason Adam with player_id, batter_count, pitcher_count, and hitter_eligible result. Hitter cache remains disabled pending log verification. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>70d19e1 -
Hitter selector: diagnostic logging + cache bypass + normalization hardening
Renames _norm_lower -> normalize_name_for_compare with whitespace collapse. Adds verbose [card_lab_hitter_diag] logging: raw candidate count/sample, pitcher_set size/sample, per-player checks for Ivan Herrera / Mick Abel / Jason Adam, and final filtered count + first 25 names. Disables @st.cache_data on _cached_hitters so every load exercises the new code path. Cache will be re-added once classification is confirmed correct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>d63e096 -
Hitter selector: dominance-based classification replacing count-only threshold
Replaces the COUNT(*) >= 20 threshold with a two-step filter: 1. Pull all batter candidates with >= 20 pitch rows 2. Build pitcher exclusion set from get_card_lab_pitchers() (already working) 3. Exclude any name in pitcher_set unless they have >= 100 batter-side pitches _norm_lower() normalizes "Last, First" → "first last" on both sides before comparison so name format differences don't cause false mismatches. Ivan Herrera passes (walk-heavy catcher, not in pitcher set). Mick Abel / Jason Adam excluded (in pitcher set, well under 100 batter pitches). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>24662ad -
Card Lab selector regression fix: count threshold + SQL sort removal
Hitter selector: replace events-filter DISTINCT with GROUP BY/HAVING COUNT(*) >= 20. The events IS NOT NULL filter excluded walk-heavy hitters (Ivan Herrera) since the events column is NULL on non-final pitches. Count threshold naturally filters pitchers with token PA without dropping any legitimate hitter. Pitcher selector: remove ORDER BY CASE WHEN from SQL. DISTINCT + CASE WHEN ORDER BY on a non-SELECT column violates CockroachDB constraints, causing the query to crash and the exception handler to return []. Python sort on the named tuples handles ordering correctly after fallback naming. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>d571e13 -
Card Lab: fix player classification, column aliases, pitcher ID flow
Root cause fixes: - Remove batter_stand/pitcher_hand/xwoba aliases that broke all model lookups (models expect stand, p_throws, estimated_woba_using_speedangle) - Restore bb.bb_type + add ec.pitcher to pitcher SELECT for feature builders - Hitter list: add events NOT IN filter to exclude pitchers with rare ABs - Pitcher list: LEFT JOIN to name map (no LIMIT), fallback "Pitcher {id}", NULL names sorted last; returns list[tuple[int,str]] keyed by pitcher ID - Pitcher window query: accept pitcher_id directly, remove name→id lookup UX/perf: - Cached hitter/pitcher list wrappers (_cached_hitters/_cached_pitchers, 10m TTL) - Game summary player filter: explicit session_state binding for rerender - Add idx_statcast_game_pk index for game summary player dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>6776cb9 -
Card Lab: fix image crash, name normalization, query pruning, DB indexes
- Replace st.image(use_container_width=True) with width=700 (ImageMixin compat) - Add normalize_name() helper: 'Last, First' -> 'First Last' for all dropdowns (format_func only — raw DB values unchanged for queries) - Game Summary player filter: remove LIMIT, 'Full Game' sentinel, normalized labels - Prune 13 unused columns from hitter query + remove plate_location JOIN - Prune plate_location JOIN from pitcher query (ax/ay/az/effective_speed/spin_axis removed) - Add idx_statcast_player_date + idx_statcast_pitcher_date to initialize_schema() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a80f497 -
Card Lab: replace HTTP statcast loaders with CockroachDB queries
- Delete _load_statcast_year + load_card_lab_source_data from app.py (OOM root cause) - Create visualization/cards/card_queries.py with full DB-backed query layer - Rewrite card_lab_page.py: no statcast_df param, DB-backed player lists, full-fidelity window queries on Generate click, staged status UX, pitcher V1 coverage note, empty-state warning instead of crash Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>826c062 -
Card Lab V1: downloadable player card generator replaces Matchups tab
Adds on-demand Kasper player card generation (Hitter / Pitcher / Game Summary) with real statcast charts, timeframe-aware data pipeline, and PNG/JPG download. All rendering is in-memory via Pillow + matplotlib. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>0b1a395