import type { Game, Team } from './apiResponseTypes' import type { Player } from './playersService' import { MODERN_STAT_ERA_START, SITE_URL, avg, obp, ops, slg, woba } from './utilities' // TODO make a stats object that has properties for current regular season, current post season, // last 4 games, historical seasons, career totals // could split into batting/pitching/fielding so only necessary ones are called to save time export interface BattingStat { player: Player team: Team game: Game | 'TOT' // stats below pa: number ab: number run: number hit: number rbi: number double: number triple: number hr: number bb: number so: number hbp: number sac: number ibb: number gidp: number sb: number cs: number bphr: number bpfo: number bp1b: number bplo: number wpa: number avg: number obp: number slg: number ops: number woba: number } interface LegacyBattingStat { player: Player team: Team pa: number, ab: number, run: number, hit: number, rbi: number, double: number, triple: number, hr: number, bb: number, so: number, hbp: number, sac: number, ibb: number, gidp: number, sb: number, cs: number, bphr: number, bpfo: number, bp1b: number, bplo: number } export async function fetchBattingStatsBySeason(seasonNumber: number, isRegularSeason: boolean): Promise { // different endpoint for pre-modern stats (/plays) era if (seasonNumber < MODERN_STAT_ERA_START) { console.warn('battingStatsService.fetchBattingStatsBySeason - Not looking up legacy season stats rn') return [] } const response = await fetch(`${SITE_URL}/api/v3/plays/batting?season=${seasonNumber}&group_by=player&limit=1000&s_type=${isRegularSeason ? 'regular' : 'post'}`) const battingStatsResponse: { count: number stats: BattingStat[] } = await response.json() if (battingStatsResponse.count === 0) return [] return battingStatsResponse.stats } export async function fetchBattingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise { // different endpoint for pre-modern stats (/plays) era if (seasonNumber < MODERN_STAT_ERA_START) { return await fetchLegacyBattingStatsBySeasonAndPlayerId(seasonNumber, playerId, isRegularSeason) } const response = await fetch(`${SITE_URL}/api/v3/plays/batting?season=${seasonNumber}&player_id=${playerId}&s_type=${isRegularSeason ? 'regular' : 'post'}`) const battingStatsResponse: { count: number stats: BattingStat[] } = await response.json() if (battingStatsResponse.count === 0) return undefined if (battingStatsResponse.count > 1) { throw new Error('battingStatsService.fetchBattingStatsBySeasonAndPlayerId - Expected one stat line for season/player, return contained many') } return battingStatsResponse.stats[0] } export async function fetchBattingStatsBySeasonAndTeamId(seasonNumber: number, teamId: number, isRegularSeason: boolean): Promise { // different endpoint for pre-modern stats (/plays) era if (seasonNumber < MODERN_STAT_ERA_START) { return [] } const response = await fetch(`${SITE_URL}/api/v3/plays/batting?season=${seasonNumber}&team_id=${teamId}&group_by=playerteam&s_type=${isRegularSeason ? 'regular' : 'post'}`) const battingStatsResponse: { count: number stats: BattingStat[] } = await response.json() if (battingStatsResponse.count === 0) return [] return battingStatsResponse.stats } async function fetchLegacyBattingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise { const response = await fetch(`${SITE_URL}/api/v3/battingstats/totals?season=${seasonNumber}&player_id=${playerId}&s_type=${isRegularSeason ? 'regular' : 'post'}`) const legacyBattingStatsResponse: { count: number stats: LegacyBattingStat[] } = await response.json() if (legacyBattingStatsResponse.count === 0) return undefined if (legacyBattingStatsResponse.count > 1) { throw new Error('battingStatsService.fetchLegacyBattingStatsBySeasonAndPlayerId - Expected one stat line for season/player, return contained many') } return makeModernBattingStatFromLegacy(legacyBattingStatsResponse.stats[0]) } export async function fetchBattingStatsForLastFourGamesBySeasonAndPlayerId(seasonNumber: number, playerId: number): Promise { const response = await fetch(`${SITE_URL}/api/v3/plays/batting?season=${seasonNumber}&player_id=${playerId}&group_by=playergame&limit=4&s_type=regular&sort=newest`) const battingStatsResponse: { count: number stats: BattingStat[] } = await response.json() if (battingStatsResponse.count > 4) { throw new Error(`battingStatsService.fetchBattingStatsForLastFourBySeasonAndPlayerId - Expected at most 4 games, return contained ${battingStatsResponse.count}`) } return battingStatsResponse.stats } export async function fetchBattingStatsBySeries(seasonNumber: number, weekNumber: number, homeTeamId: number, awayTeamId: number): Promise { // no support for pre-modern games yet if (seasonNumber < MODERN_STAT_ERA_START) { return [] } const response = await fetch(`${SITE_URL}/api/v3/plays/batting?season=${seasonNumber}&week=${weekNumber}&team_id=${homeTeamId}&team_id=${awayTeamId}&group_by=playergame`) const battingStatsResponse: { count: number stats: BattingStat[] } = await response.json() return battingStatsResponse.stats } export function aggregateBattingStats(battingStats: BattingStat[]): BattingStat { const totalStat: BattingStat = { player: battingStats[0].player, team: battingStats[0].team, game: 'TOT', // stats below pa: 0, ab: 0, run: 0, hit: 0, rbi: 0, double: 0, triple: 0, hr: 0, bb: 0, so: 0, hbp: 0, sac: 0, ibb: 0, gidp: 0, sb: 0, cs: 0, bphr: 0, bpfo: 0, bp1b: 0, bplo: 0, wpa: 0, avg: 0, obp: 0, slg: 0, ops: 0, woba: 0 } battingStats.forEach(stat => { totalStat.pa += stat.pa totalStat.ab += stat.ab totalStat.run += stat.run totalStat.hit += stat.hit totalStat.rbi += stat.rbi totalStat.double += stat.double totalStat.triple += stat.triple totalStat.hr += stat.hr totalStat.bb += stat.bb totalStat.so += stat.so totalStat.hbp += stat.hbp totalStat.sac += stat.sac totalStat.ibb += stat.ibb totalStat.gidp += stat.gidp totalStat.sb += stat.sb totalStat.cs += stat.cs totalStat.bphr += stat.bphr totalStat.bpfo += stat.bpfo totalStat.bp1b += stat.bp1b totalStat.bplo += stat.bplo }) return { ...totalStat, avg: avg(totalStat), obp: obp(totalStat), slg: slg(totalStat), ops: ops(totalStat), woba: woba(totalStat) } } function makeModernBattingStatFromLegacy(legacyStat: LegacyBattingStat): BattingStat { return { player: legacyStat.player, team: legacyStat.team, game: 'TOT', // stats below pa: legacyStat.pa, ab: legacyStat.ab, run: legacyStat.run, hit: legacyStat.hit, rbi: legacyStat.rbi, double: legacyStat.double, triple: legacyStat.triple, hr: legacyStat.hr, bb: legacyStat.bb, so: legacyStat.so, hbp: legacyStat.hbp, sac: legacyStat.sac, ibb: legacyStat.ibb, gidp: legacyStat.gidp, sb: legacyStat.sb, cs: legacyStat.cs, bphr: legacyStat.bphr, bpfo: legacyStat.bpfo, bp1b: legacyStat.bp1b, bplo: legacyStat.bplo, wpa: 0, avg: avg(legacyStat), obp: obp(legacyStat), slg: slg(legacyStat), ops: ops(legacyStat), woba: woba(legacyStat) } }