Codex commited on
Commit
9d4572d
·
1 Parent(s): 9a27dc9

Fix hitter chart profile scoring

Browse files
Files changed (1) hide show
  1. src/matchups.js +47 -4
src/matchups.js CHANGED
@@ -2565,15 +2565,20 @@ export class MatchupService {
2565
  throw new Error('No hosted hitter slate was available in the fallback window.');
2566
  }
2567
 
2568
- const cacheKey = this.buildChartCacheKey('hitter-chart-base', { date: resolvedDate });
2569
  const base = await this.getCachedChartValue(cacheKey, async () => {
2570
- const [slateRows, hitterRows, pitcherRows, rosterRows] = await Promise.all([
2571
  this.hosted.readDailyFile(resolvedDate, 'slate.parquet', SLATE_COLUMNS),
2572
  this.hosted.readDailyFile(resolvedDate, 'daily_hitter_metrics.parquet', HITTER_COLUMNS),
2573
  this.hosted.readDailyFile(resolvedDate, 'daily_pitcher_metrics.parquet', PITCHER_COLUMNS),
 
2574
  this.hosted.readDailyFile(resolvedDate, 'rosters.parquet', ROSTER_COLUMNS).catch(() => []),
 
 
2575
  ]);
2576
  const rosterLookup = buildRosterLookup(rosterRows);
 
 
2577
  const pitcherLookup = new Map(
2578
  pitcherRows
2579
  .filter((row) => keepRowForDefaults(row))
@@ -2586,12 +2591,50 @@ export class MatchupService {
2586
  slateLookup.set(team, { ...slate, opposingPitcherHand: pitcher.p_throws });
2587
  }
2588
  }
2589
- return hitterRows
 
2590
  .filter((row) => keepRowForDefaults(row))
2591
  .map((row) => withDefaults(row, slateLookup, { rosterLookup, playerType: 'hitter' }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2592
  });
2593
 
2594
- const hitterMatch = findBestPlayerMatch(base, 'hitter_name', playerName);
 
2595
  if (!hitterMatch) {
2596
  throw new Error(`No hitter matchup profile matched "${playerName}".`);
2597
  }
 
2565
  throw new Error('No hosted hitter slate was available in the fallback window.');
2566
  }
2567
 
2568
+ const cacheKey = this.buildChartCacheKey('hitter-chart-base', { date: resolvedDate, player: playerName });
2569
  const base = await this.getCachedChartValue(cacheKey, async () => {
2570
+ const [slateRows, hitterRows, pitcherRows, exclusionRows, rosterRows, batterZoneRows, pitcherZoneRows] = await Promise.all([
2571
  this.hosted.readDailyFile(resolvedDate, 'slate.parquet', SLATE_COLUMNS),
2572
  this.hosted.readDailyFile(resolvedDate, 'daily_hitter_metrics.parquet', HITTER_COLUMNS),
2573
  this.hosted.readDailyFile(resolvedDate, 'daily_pitcher_metrics.parquet', PITCHER_COLUMNS),
2574
+ this.hosted.readDailyFile(resolvedDate, 'hitter_pitcher_exclusions.parquet', EXCLUSION_COLUMNS).catch(() => []),
2575
  this.hosted.readDailyFile(resolvedDate, 'rosters.parquet', ROSTER_COLUMNS).catch(() => []),
2576
+ this.hosted.readDailyFile(resolvedDate, 'daily_batter_zone_profiles.parquet', BATTER_ZONE_COLUMNS).catch(() => []),
2577
+ this.hosted.readDailyFile(resolvedDate, 'daily_pitcher_zone_profiles.parquet', PITCHER_ZONE_COLUMNS).catch(() => []),
2578
  ]);
2579
  const rosterLookup = buildRosterLookup(rosterRows);
2580
+ const rosterIdsByTeam = buildRosterIdsByTeam(rosterRows);
2581
+ const exclusionSet = buildExclusionSet(exclusionRows);
2582
  const pitcherLookup = new Map(
2583
  pitcherRows
2584
  .filter((row) => keepRowForDefaults(row))
 
2591
  slateLookup.set(team, { ...slate, opposingPitcherHand: pitcher.p_throws });
2592
  }
2593
  }
2594
+
2595
+ const preparedRows = hitterRows
2596
  .filter((row) => keepRowForDefaults(row))
2597
  .map((row) => withDefaults(row, slateLookup, { rosterLookup, playerType: 'hitter' }));
2598
+
2599
+ const matchedPlayer = findBestPlayerMatch(preparedRows, 'hitter_name', playerName);
2600
+ if (!matchedPlayer) {
2601
+ return {
2602
+ resolvedDate,
2603
+ rows: [],
2604
+ scoredRows: [],
2605
+ batterZoneRows,
2606
+ pitcherZoneRows,
2607
+ };
2608
+ }
2609
+
2610
+ const normalizedTeam = normalizeTeam(matchedPlayer.team);
2611
+ const effectiveSplit = matchedPlayer.opposing_pitcher_hand === 'R'
2612
+ ? 'vs_rhp'
2613
+ : matchedPlayer.opposing_pitcher_hand === 'L'
2614
+ ? 'vs_lhp'
2615
+ : DEFAULT_SPLIT_KEY;
2616
+
2617
+ const teamRows = hitterRows
2618
+ .filter((row) => normalizeTeam(row.team) === normalizedTeam)
2619
+ .filter((row) => String(row.split_key ?? DEFAULT_SPLIT_KEY) === effectiveSplit)
2620
+ .filter((row) => String(row.recent_window ?? DEFAULT_RECENT_WINDOW) === DEFAULT_RECENT_WINDOW)
2621
+ .filter((row) => String(row.weighted_mode ?? DEFAULT_WEIGHTED_MODE) === DEFAULT_WEIGHTED_MODE)
2622
+ .filter((row) => {
2623
+ const rosterPlayerIds = rosterIdsByTeam.get(normalizedTeam) ?? null;
2624
+ return !rosterPlayerIds || rosterPlayerIds.has(String(row.batter ?? row.player_id ?? '').trim());
2625
+ })
2626
+ .filter((row) => !exclusionSet.has(String(row.batter ?? row.player_id ?? '').trim()))
2627
+ .map((row) => withDefaults(row, slateLookup, { rosterLookup, playerType: 'hitter' }));
2628
+
2629
+ return {
2630
+ resolvedDate,
2631
+ rows: preparedRows,
2632
+ scoredRows: addHitterMatchupScore(teamRows, batterZoneRows, pitcherZoneRows),
2633
+ };
2634
  });
2635
 
2636
+ const hitterMatch = findBestPlayerMatch(base.scoredRows ?? [], 'hitter_name', playerName)
2637
+ ?? findBestPlayerMatch(base.rows ?? [], 'hitter_name', playerName);
2638
  if (!hitterMatch) {
2639
  throw new Error(`No hitter matchup profile matched "${playerName}".`);
2640
  }