Add fielding stats to player page
This commit is contained in:
parent
bd0bafe705
commit
d00b61684e
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -20,6 +20,7 @@ declare module '@vue/runtime-core' {
|
||||
NewsPreview: typeof import('./src/components/NewsPreview.vue')['default']
|
||||
PlayerBattingSummaryTable: typeof import('./src/components/PlayerBattingSummaryTable.vue')['default']
|
||||
PlayerCareerBattingTable: typeof import('./src/components/PlayerCareerBattingTable.vue')['default']
|
||||
PlayerCareerFieldingTable: typeof import('./src/components/PlayerCareerFieldingTable.vue')['default']
|
||||
PlayerCareerPitchingTable: typeof import('./src/components/PlayerCareerPitchingTable.vue')['default']
|
||||
PlayerPitchingSummaryTable: typeof import('./src/components/PlayerPitchingSummaryTable.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
||||
@ -26,6 +26,12 @@
|
||||
<th>HBP</th>
|
||||
<th>SAC</th>
|
||||
<th>IBB</th>
|
||||
<th>XCh</th>
|
||||
<th>XH</th>
|
||||
<th>E</th>
|
||||
<th>PB</th>
|
||||
<th>SBa</th>
|
||||
<th>CSc</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="last4-batting">
|
||||
@ -51,6 +57,12 @@
|
||||
<td>{{ gameStat.hbp }}</td>
|
||||
<td>{{ gameStat.sac }}</td>
|
||||
<td>{{ gameStat.ibb }}</td>
|
||||
<td>{{ fieldingStatForGame(gameStat).xch }}</td>
|
||||
<td>{{ fieldingStatForGame(gameStat).xhit }}</td>
|
||||
<td>{{ fieldingStatForGame(gameStat).error }}</td>
|
||||
<td>{{ fieldingStatForGame(gameStat).pb }}</td>
|
||||
<td>{{ fieldingStatForGame(gameStat).sba }}</td>
|
||||
<td>{{ fieldingStatForGame(gameStat).cs }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -61,17 +73,52 @@
|
||||
<script lang="ts">
|
||||
import type { Game } from '@/services/apiResponseTypes'
|
||||
import type { BattingStat } from '@/services/battingStatsService'
|
||||
import type { FieldingStat } from '@/services/fieldingStatsService'
|
||||
|
||||
interface MinimalAggregateFieldingStat {
|
||||
xch: number,
|
||||
xhit: number,
|
||||
error: number,
|
||||
pb: number,
|
||||
sba: number,
|
||||
cs: number
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "LastFourGamesBattingTable",
|
||||
props: {
|
||||
last4GamesBatting: { type: Array<BattingStat>, required: true }
|
||||
last4GamesBatting: { type: Array<BattingStat>, required: true },
|
||||
last4GamesFielding: { type: Array<FieldingStat>, required: true }
|
||||
},
|
||||
methods: {
|
||||
makeWxGyFromGame(game: Game | 'TOT'): string {
|
||||
if (game === 'TOT') return 'TOT'
|
||||
return `w${game.week}g${game.game_num}`
|
||||
},
|
||||
fieldingStatForGame(battingStat: BattingStat): MinimalAggregateFieldingStat {
|
||||
const fieldingStat: MinimalAggregateFieldingStat = {
|
||||
xch: 0,
|
||||
xhit: 0,
|
||||
error: 0,
|
||||
pb: 0,
|
||||
sba: 0,
|
||||
cs: 0
|
||||
}
|
||||
|
||||
this.last4GamesFielding.forEach(stat => {
|
||||
if (stat.game === 'TOT' || battingStat.game === 'TOT') return
|
||||
if (stat.game.id !== battingStat.game.id) return
|
||||
|
||||
fieldingStat.xch += stat.xCheckCount
|
||||
fieldingStat.xhit += stat.hit
|
||||
fieldingStat.error += stat.error
|
||||
fieldingStat.pb += stat.passedBallCount
|
||||
fieldingStat.sba += stat.stolenBaseCheckCount
|
||||
fieldingStat.cs += stat.caughtStealingCount
|
||||
})
|
||||
|
||||
return fieldingStat
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
150
src/components/PlayerCareerFieldingTable.vue
Normal file
150
src/components/PlayerCareerFieldingTable.vue
Normal file
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div v-if="hasFieldingStats" class="row" id="career-fielding-row">
|
||||
<div class="col-sm-8">
|
||||
<h3>Fielding Stats</h3>
|
||||
<div class="table-responsive-xl">
|
||||
<table class="table table-sm table-striped" id="career-fielding">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>Season</th>
|
||||
<th>Pos</th>
|
||||
<th>XCh</th>
|
||||
<th>XH</th>
|
||||
<th>E</th>
|
||||
<th>PB</th>
|
||||
<th>SBa</th>
|
||||
<th>CSc</th>
|
||||
<th>CS%</th>
|
||||
<th>wF%</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="career-fielding-table">
|
||||
<tr v-for="stat in sortedRegularAndPostSeasonFielding">
|
||||
<td>S{{ stat.seasonNumber }}{{ stat.isRegularSeason ? '' : ' / Playoffs' }}</td>
|
||||
<td>{{ stat.pos }}</td>
|
||||
<td>{{ stat.xCheckCount }}</td>
|
||||
<td>{{ stat.hit }}</td>
|
||||
<td>{{ stat.error }}</td>
|
||||
<td>{{ stat.passedBallCount }}</td>
|
||||
<td>{{ stat.stolenBaseCheckCount }}</td>
|
||||
<td>{{ stat.caughtStealingCount }}</td>
|
||||
<td>{{ formatCaughtStealingPercent(stat) }}</td>
|
||||
<td>{{ formatWeightedFieldingPercent(stat) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr v-for="stat in sortedCareerFieldingStats" id="career-fielding-footer">
|
||||
<th>Career</th>
|
||||
<th>{{ stat.pos }}</th>
|
||||
<th>{{ stat.xCheckCount }}</th>
|
||||
<th>{{ stat.hit }}</th>
|
||||
<th>{{ stat.error }}</th>
|
||||
<th>{{ stat.passedBallCount }}</th>
|
||||
<th>{{ stat.stolenBaseCheckCount }}</th>
|
||||
<th>{{ stat.caughtStealingCount }}</th>
|
||||
<th>{{ formatCaughtStealingPercent(stat) }}</th>
|
||||
<th>{{ formatWeightedFieldingPercent(stat) }}</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { aggregateFieldingStats, type FieldingStat } from '@/services/fieldingStatsService'
|
||||
|
||||
interface FieldingStatWithSeason extends FieldingStat {
|
||||
seasonNumber: number
|
||||
isRegularSeason: boolean
|
||||
}
|
||||
|
||||
const POS_MAP = {
|
||||
'P': 1,
|
||||
'C': 2,
|
||||
'1B': 3,
|
||||
'2B': 4,
|
||||
'3B': 5,
|
||||
'SS': 6,
|
||||
'LF': 7,
|
||||
'CF': 8,
|
||||
'RF': 9,
|
||||
'TOT': 10
|
||||
}
|
||||
|
||||
function compareFieldingStats(s1: FieldingStatWithSeason, s2: FieldingStatWithSeason): number {
|
||||
if (s1.seasonNumber - s2.seasonNumber !== 0) {
|
||||
return s1.seasonNumber - s2.seasonNumber
|
||||
}
|
||||
|
||||
if (s1.isRegularSeason !== s2.isRegularSeason) {
|
||||
return s1.isRegularSeason
|
||||
? -1
|
||||
: 1
|
||||
}
|
||||
|
||||
// at this point stats are same season and also both regular or post season stats
|
||||
// and must be sorted on position order
|
||||
return POS_MAP[s1.pos] - POS_MAP[s2.pos]
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "PlayerCareerFieldingTable",
|
||||
props: {
|
||||
regularSeasonFieldingStats: { type: Array<FieldingStat>, required: true },
|
||||
postSeasonFieldingStats: { type: Array<FieldingStat>, required: true }
|
||||
},
|
||||
computed: {
|
||||
hasFieldingStats(): boolean {
|
||||
return !!(this.regularSeasonFieldingStats.length + this.postSeasonFieldingStats.length)
|
||||
},
|
||||
sortedCareerFieldingStats(): FieldingStat[] {
|
||||
if (this.regularSeasonFieldingStats.length > 0) {
|
||||
// old site behavior just summed regular season stats for the career line total
|
||||
return aggregateFieldingStats(this.regularSeasonFieldingStats)
|
||||
.sort((a, b) => POS_MAP[a.pos] - POS_MAP[b.pos]) //only need to sort careers totals by position
|
||||
}
|
||||
|
||||
return []
|
||||
},
|
||||
sortedRegularAndPostSeasonFielding(): FieldingStatWithSeason[] {
|
||||
let seasonStats: FieldingStatWithSeason[] = []
|
||||
|
||||
if (this.regularSeasonFieldingStats?.length) {
|
||||
seasonStats = seasonStats.concat(this.regularSeasonFieldingStats.map(stat => {
|
||||
return {
|
||||
...stat,
|
||||
seasonNumber: stat.player.season,
|
||||
isRegularSeason: true
|
||||
}
|
||||
}))
|
||||
}
|
||||
// TODO: here would be where you could filter out postseason stats if desired (like Josef requested)
|
||||
if (this.postSeasonFieldingStats?.length) {
|
||||
seasonStats = seasonStats.concat(this.postSeasonFieldingStats.map(stat => {
|
||||
return {
|
||||
...stat,
|
||||
seasonNumber: stat.player.season,
|
||||
isRegularSeason: false
|
||||
}
|
||||
}))
|
||||
}
|
||||
// TODO: additionally, fielding stats should sort on position P, C, 1B, ..., CF, RF
|
||||
return seasonStats.sort(compareFieldingStats)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatCaughtStealingPercent(stat: FieldingStat): string {
|
||||
if (stat.stolenBaseCheckCount === 0 || Number.isNaN(stat.caughtStealingPercent)) {
|
||||
return '-'
|
||||
}
|
||||
|
||||
return `${(stat.caughtStealingPercent * 100).toFixed(1)}%`
|
||||
},
|
||||
formatWeightedFieldingPercent(stat: FieldingStat): string {
|
||||
return stat.weightedFieldingPercent.toFixed(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -64,38 +64,6 @@ interface LegacyBattingStat {
|
||||
bplo: number
|
||||
}
|
||||
|
||||
export interface FieldingStat {
|
||||
player: Player
|
||||
team: Team
|
||||
pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT'
|
||||
xCheckCount: number
|
||||
hit: number
|
||||
error: number
|
||||
stolenBaseCheckCount: number
|
||||
stolenBaseCount: number
|
||||
caughtStealingCount: number
|
||||
passedBallCount: number
|
||||
winProbabilityAdded: number
|
||||
weightedFieldingPercent: number
|
||||
caughtStealingPercent: number | ''
|
||||
}
|
||||
|
||||
export interface FieldingStatRaw {
|
||||
player: Player
|
||||
team: Team
|
||||
pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT'
|
||||
"x-ch": number
|
||||
hit: number
|
||||
error: number
|
||||
"sb-ch": number
|
||||
sb: number
|
||||
cs: number
|
||||
pb: number
|
||||
wpa: number
|
||||
"wf%": number
|
||||
"cs%": number | ''
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -136,7 +104,7 @@ async function fetchLegacyBattingStatsBySeasonAndPlayerId(seasonNumber: number,
|
||||
}
|
||||
|
||||
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&sort=newest`)
|
||||
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
|
||||
|
||||
193
src/services/fieldingStatsService.ts
Normal file
193
src/services/fieldingStatsService.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import type { Game, Team } from './apiResponseTypes'
|
||||
import type { Player } from './playersService'
|
||||
import { MODERN_STAT_ERA_START, SITE_URL } 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 FieldingStat {
|
||||
game: Game | 'TOT'
|
||||
player: Player
|
||||
team: Team
|
||||
pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT'
|
||||
xCheckCount: number
|
||||
hit: number
|
||||
error: number
|
||||
stolenBaseCheckCount: number
|
||||
stolenBaseCount: number
|
||||
caughtStealingCount: number
|
||||
passedBallCount: number
|
||||
winProbabilityAdded: number
|
||||
weightedFieldingPercent: number
|
||||
caughtStealingPercent: number
|
||||
}
|
||||
|
||||
interface FieldingStatRaw {
|
||||
game: Game | 'TOT'
|
||||
player: Player
|
||||
team: Team
|
||||
pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT'
|
||||
"x-ch": number
|
||||
hit: number
|
||||
error: number
|
||||
"sb-ch": number
|
||||
sb: number
|
||||
cs: number
|
||||
pb: number
|
||||
wpa: number
|
||||
"wf%": number
|
||||
"cs%": number | ''
|
||||
}
|
||||
|
||||
interface LegacyFieldingStat {
|
||||
player: Player
|
||||
team: Team
|
||||
pos: 'P' | 'C' | '1B' | '2B' | '3B' | 'SS' | 'LF' | 'CF' | 'RF' | 'TOT'
|
||||
xch: number
|
||||
xhit: number
|
||||
error: number
|
||||
pb: number
|
||||
sbc: number
|
||||
csc: number
|
||||
}
|
||||
|
||||
export async function fetchFieldingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise<FieldingStat[]> {
|
||||
// different endpoint for pre-modern stats (/plays) era
|
||||
if (seasonNumber < MODERN_STAT_ERA_START) {
|
||||
return await fetchLegacyFieldingStatsBySeasonAndPlayerId(seasonNumber, playerId, isRegularSeason)
|
||||
}
|
||||
|
||||
const response = await fetch(`${SITE_URL}/api/v3/plays/fielding?season=${seasonNumber}&player_id=${playerId}&group_by=playerposition&s_type=${isRegularSeason ? 'regular' : 'post'}`)
|
||||
|
||||
const fieldingStatsResponse: {
|
||||
count: number
|
||||
stats: FieldingStatRaw[]
|
||||
} = await response.json()
|
||||
|
||||
if (fieldingStatsResponse.count === 0) return []
|
||||
|
||||
|
||||
|
||||
return fieldingStatsResponse.stats.map(normalizeFieldingStat)
|
||||
}
|
||||
|
||||
async function fetchLegacyFieldingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise<FieldingStat[]> {
|
||||
const response = await fetch(`${SITE_URL}/api/v3/fieldingstats/totals?season=${seasonNumber}&player_id=${playerId}&s_type=${isRegularSeason ? 'regular' : 'post'}`)
|
||||
|
||||
const legacyFieldingStatsResponse: {
|
||||
count: number
|
||||
stats: LegacyFieldingStat[]
|
||||
} = await response.json()
|
||||
|
||||
if (legacyFieldingStatsResponse.count === 0) return []
|
||||
|
||||
return legacyFieldingStatsResponse.stats.map(makeModernFieldingStatFromLegacy)
|
||||
}
|
||||
|
||||
export async function fetchFieldingStatsForLastFourGamesBySeasonAndPlayerId(seasonNumber: number, playerId: number): Promise<FieldingStat[]> {
|
||||
const response = await fetch(`${SITE_URL}/api/v3/plays/fielding?season=${seasonNumber}&player_id=${playerId}&group_by=playerpositiongame&limit=4&s_type=regular&sort=newest`)
|
||||
|
||||
const fieldingStatsResponse: {
|
||||
count: number
|
||||
stats: FieldingStatRaw[]
|
||||
} = await response.json()
|
||||
|
||||
if (fieldingStatsResponse.count > 4) {
|
||||
throw new Error(`fieldingStatsService.fetchFieldingStatsForLastFourBySeasonAndPlayerId - Expected at most 4 games, return contained ${fieldingStatsResponse.count}`)
|
||||
}
|
||||
|
||||
return fieldingStatsResponse.stats.map(normalizeFieldingStat)
|
||||
}
|
||||
|
||||
export function aggregateFieldingStats(fieldingStats: FieldingStat[]): FieldingStat[] {
|
||||
const fieldingStatsByPosition = fieldingStats.reduce((statsByPos: { [key: string]: FieldingStat }, stat: FieldingStat) => {
|
||||
if (!statsByPos[stat.pos]) {
|
||||
statsByPos[stat.pos] = {
|
||||
game: 'TOT',
|
||||
player: stat.player,
|
||||
team: stat.team,
|
||||
pos: stat.pos,
|
||||
xCheckCount: stat.xCheckCount,
|
||||
hit: stat.hit,
|
||||
error: stat.error,
|
||||
stolenBaseCheckCount: stat.stolenBaseCheckCount,
|
||||
stolenBaseCount: stat.stolenBaseCount,
|
||||
caughtStealingCount: stat.caughtStealingCount,
|
||||
passedBallCount: stat.passedBallCount,
|
||||
winProbabilityAdded: stat.winProbabilityAdded,
|
||||
weightedFieldingPercent: stat.weightedFieldingPercent,
|
||||
caughtStealingPercent: stat.caughtStealingPercent
|
||||
}
|
||||
} else {
|
||||
statsByPos[stat.pos].xCheckCount += stat.xCheckCount
|
||||
statsByPos[stat.pos].hit += stat.hit
|
||||
statsByPos[stat.pos].error += stat.error
|
||||
statsByPos[stat.pos].stolenBaseCheckCount += stat.stolenBaseCheckCount
|
||||
statsByPos[stat.pos].stolenBaseCount += stat.stolenBaseCount
|
||||
statsByPos[stat.pos].caughtStealingCount += stat.caughtStealingCount
|
||||
statsByPos[stat.pos].passedBallCount += stat.passedBallCount
|
||||
statsByPos[stat.pos].winProbabilityAdded += stat.winProbabilityAdded
|
||||
statsByPos[stat.pos].weightedFieldingPercent = weightedFieldingPercentage({
|
||||
xChecks: statsByPos[stat.pos].xCheckCount,
|
||||
hits: statsByPos[stat.pos].hit,
|
||||
errors: statsByPos[stat.pos].error
|
||||
})
|
||||
statsByPos[stat.pos].caughtStealingPercent = caughtStealingPercentage({
|
||||
stolenBaseAttempts: statsByPos[stat.pos].stolenBaseCheckCount,
|
||||
caughtStealingCount: statsByPos[stat.pos].caughtStealingCount
|
||||
})
|
||||
}
|
||||
|
||||
return statsByPos
|
||||
}, {})
|
||||
return Object.values(fieldingStatsByPosition)
|
||||
}
|
||||
|
||||
function makeModernFieldingStatFromLegacy(legacyStat: LegacyFieldingStat): FieldingStat {
|
||||
return {
|
||||
game: 'TOT',
|
||||
player: legacyStat.player,
|
||||
team: legacyStat.team,
|
||||
pos: legacyStat.pos,
|
||||
xCheckCount: legacyStat.xch,
|
||||
hit: legacyStat.xhit,
|
||||
error: legacyStat.error,
|
||||
stolenBaseCheckCount: legacyStat.sbc,
|
||||
stolenBaseCount: legacyStat.sbc - legacyStat.csc,
|
||||
caughtStealingCount: legacyStat.csc,
|
||||
passedBallCount: legacyStat.pb,
|
||||
winProbabilityAdded: 0,
|
||||
weightedFieldingPercent: weightedFieldingPercentage({ xChecks: legacyStat.xch, hits: legacyStat.xhit, errors: legacyStat.error }),
|
||||
caughtStealingPercent: caughtStealingPercentage({ stolenBaseAttempts: legacyStat.sbc, caughtStealingCount: legacyStat.csc })
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeFieldingStat(rawStat: FieldingStatRaw): FieldingStat {
|
||||
return {
|
||||
game: rawStat.game,
|
||||
player: rawStat.player,
|
||||
team: rawStat.team,
|
||||
pos: rawStat.pos,
|
||||
xCheckCount: rawStat['x-ch'],
|
||||
hit: rawStat.hit,
|
||||
error: rawStat.error,
|
||||
stolenBaseCheckCount: rawStat['sb-ch'],
|
||||
stolenBaseCount: rawStat.sb,
|
||||
caughtStealingCount: rawStat.cs,
|
||||
passedBallCount: rawStat.pb,
|
||||
winProbabilityAdded: rawStat.wpa,
|
||||
weightedFieldingPercent: rawStat['wf%'],
|
||||
caughtStealingPercent: rawStat['cs%'] === '' ? NaN : rawStat['cs%']
|
||||
}
|
||||
}
|
||||
|
||||
export function weightedFieldingPercentage(stat: { xChecks: number, hits: number, errors: number }): number {
|
||||
if (stat.xChecks === 0) return NaN
|
||||
return (stat.xChecks - stat.errors * 0.5 - stat.hits * 0.75) / stat.xChecks
|
||||
}
|
||||
|
||||
export function caughtStealingPercentage(stat: { stolenBaseAttempts: number, caughtStealingCount: number }): number {
|
||||
if (stat.stolenBaseAttempts === 0) return NaN
|
||||
return stat.caughtStealingCount / stat.stolenBaseAttempts
|
||||
}
|
||||
@ -170,7 +170,7 @@ async function fetchLegacyPitchingStatsBySeasonAndPlayerId(seasonNumber: number,
|
||||
}
|
||||
|
||||
export async function fetchPitchingStatsForLastFourGamesBySeasonAndPlayerId(seasonNumber: number, playerId: number): Promise<PitchingStat[]> {
|
||||
const response = await fetch(`${SITE_URL}/api/v3/plays/pitching?season=${seasonNumber}&player_id=${playerId}&group_by=playergame&limit=4&sort=newest`)
|
||||
const response = await fetch(`${SITE_URL}/api/v3/plays/pitching?season=${seasonNumber}&player_id=${playerId}&group_by=playergame&limit=4&s_type=regular&sort=newest`)
|
||||
|
||||
const pitchingStatsResponse: {
|
||||
count: number
|
||||
|
||||
@ -64,17 +64,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Last 4 Games -->
|
||||
<LastFourGamesBattingTable v-if="isBatter" :last4-games-batting="last4GamesBatting" />
|
||||
<LastFourGamesBattingTable v-if="isBatter" :last4-games-batting="last4GamesBatting"
|
||||
:last4-games-fielding="last4GamesFielding" />
|
||||
<LastFourGamesPitchingTable v-else :last4-games-pitching="last4GamesPitching" />
|
||||
</div>
|
||||
|
||||
<!-- Career Batting -->
|
||||
<!-- Career Stats -->
|
||||
<PlayerCareerBattingTable v-if="isBatter" :regular-season-batting-stats="regularSeasonBattingStats"
|
||||
:post-season-batting-stats="postSeasonBattingStats" />
|
||||
<PlayerCareerPitchingTable :regular-season-pitching-stats="regularSeasonPitchingStats"
|
||||
:post-season-pitching-stats="postSeasonPitchingStats" />
|
||||
<PlayerCareerBattingTable v-if="!isBatter" :regular-season-batting-stats="regularSeasonBattingStats"
|
||||
:post-season-batting-stats="postSeasonBattingStats" />
|
||||
<PlayerCareerFieldingTable :regular-season-fielding-stats="regularSeasonFieldingStats"
|
||||
:post-season-fielding-stats="postSeasonFieldingStats" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -89,9 +92,11 @@ import LastFourGamesBattingTable from '@/components/LastFourGamesBattingTable.vu
|
||||
import LastFourGamesPitchingTable from '@/components/LastFourGamesPitchingTable.vue'
|
||||
import PlayerCareerBattingTable from '@/components/PlayerCareerBattingTable.vue'
|
||||
import PlayerCareerPitchingTable from '@/components/PlayerCareerPitchingTable.vue'
|
||||
import PlayerCareerFieldingTable from '@/components/PlayerCareerFieldingTable.vue'
|
||||
import PlayerBattingSummaryTable from '@/components/PlayerBattingSummaryTable.vue'
|
||||
import PlayerPitchingSummaryTable from '@/components/PlayerPitchingSummaryTable.vue'
|
||||
import { fetchPitchingStatsBySeasonAndPlayerId, fetchPitchingStatsForLastFourGamesBySeasonAndPlayerId, type PitchingStat } from '@/services/pitchingStatsService'
|
||||
import { fetchFieldingStatsBySeasonAndPlayerId, fetchFieldingStatsForLastFourGamesBySeasonAndPlayerId, type FieldingStat } from '@/services/fieldingStatsService'
|
||||
|
||||
export default {
|
||||
name: "PlayerView",
|
||||
@ -108,6 +113,10 @@ export default {
|
||||
regularSeasonPitchingStats: [] as PitchingStat[],
|
||||
postSeasonPitchingStats: [] as PitchingStat[],
|
||||
last4GamesPitching: [] as PitchingStat[],
|
||||
// Fielding stats
|
||||
regularSeasonFieldingStats: [] as FieldingStat[],
|
||||
postSeasonFieldingStats: [] as FieldingStat[],
|
||||
last4GamesFielding: [] as FieldingStat[],
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@ -116,7 +125,8 @@ export default {
|
||||
LastFourGamesBattingTable,
|
||||
PlayerPitchingSummaryTable,
|
||||
PlayerCareerPitchingTable,
|
||||
LastFourGamesPitchingTable
|
||||
LastFourGamesPitchingTable,
|
||||
PlayerCareerFieldingTable
|
||||
},
|
||||
props: {
|
||||
seasonNumber: { type: Number, required: true },
|
||||
@ -223,6 +233,7 @@ export default {
|
||||
this.last2Decisions = await fetchLast2DecisionsByPlayerId(this.seasonNumber, this.player.id)
|
||||
this.last4GamesBatting = await fetchBattingStatsForLastFourGamesBySeasonAndPlayerId(this.seasonNumber, this.player.id)
|
||||
this.last4GamesPitching = await fetchPitchingStatsForLastFourGamesBySeasonAndPlayerId(this.seasonNumber, this.player.id)
|
||||
this.last4GamesFielding = await fetchFieldingStatsForLastFourGamesBySeasonAndPlayerId(this.seasonNumber, this.player.id)
|
||||
|
||||
// TODO: this should change, either with an api that can take a player name for every season, a way
|
||||
// to get multiple seasons stats at once, or a players ids across all seasons at once
|
||||
@ -231,6 +242,8 @@ export default {
|
||||
this.postSeasonBattingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchBattingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).filter(isNotUndefined)
|
||||
this.regularSeasonPitchingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchPitchingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).filter(isNotUndefined)
|
||||
this.postSeasonPitchingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchPitchingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).filter(isNotUndefined)
|
||||
this.regularSeasonFieldingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchFieldingStatsBySeasonAndPlayerId(player!.season, player!.id, true)))).flatMap(stat => stat).filter(isNotUndefined)
|
||||
this.postSeasonFieldingStats = (await Promise.all(playerSeasons.filter(isNotUndefined).map(player => fetchFieldingStatsBySeasonAndPlayerId(player!.season, player!.id, false)))).flatMap(stat => stat).filter(isNotUndefined)
|
||||
},
|
||||
async tryFetchPlayerByNameForAnySeason(seasonNumber: number, playerName: string): Promise<Player | undefined> {
|
||||
do {
|
||||
|
||||
@ -8,7 +8,12 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "ES2022",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM"
|
||||
],
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user