275 lines
7.6 KiB
TypeScript
275 lines
7.6 KiB
TypeScript
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<BattingStat[]> {
|
|
// 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<BattingStat | undefined> {
|
|
// 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<BattingStat[]> {
|
|
// 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<BattingStat | undefined> {
|
|
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<BattingStat[]> {
|
|
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<BattingStat[]> {
|
|
// 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)
|
|
}
|
|
}
|