Add team pitching table

This commit is contained in:
Peter 2023-09-19 15:46:02 -04:00
parent 0ca9a9e8b3
commit 672ef8ece1
7 changed files with 209 additions and 12 deletions

1
components.d.ts vendored
View File

@ -26,6 +26,7 @@ declare module '@vue/runtime-core' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
StandingsTable: typeof import('./src/components/StandingsTable.vue')['default']
TeamPitchingTable: typeof import('./src/components/TeamPitchingTable.vue')['default']
TeamScheduleTable: typeof import('./src/components/TeamScheduleTable.vue')['default']
}
}

View File

@ -108,7 +108,7 @@
<script lang="ts">
import { aggregatePitchingStats, type PitchingStat } from '@/services/pitchingStatsService'
import { outsToInnings } from '@/services/utilities'
import { hitsPer9, hrsPer9, outsToInnings, winPercentage } from '@/services/utilities'
interface PitchingStatWithSeason extends PitchingStat {
seasonNumber: number
@ -170,19 +170,13 @@ export default {
return outsToInnings(stat)
},
winPercentage(stat: PitchingStat): string {
if (stat.win + stat.loss === 0) return '-'
return (stat.win / (stat.win + stat.loss)).toFixed(3)
return winPercentage(stat)
},
hitsPer9(stat: PitchingStat): string {
if (stat.outs === 0) return '-'
return (stat.hits * 27 / stat.outs).toFixed(1)
return hitsPer9(stat)
},
hrsPer9(stat: PitchingStat): string {
if (stat.outs === 0) return '-'
return (stat.hr * 27 / stat.outs).toFixed(1)
return hrsPer9(stat)
}
}
}

View File

@ -0,0 +1,163 @@
<template>
<div v-if="pitchingStats.length" class="row">
<div class="col-sm-12">
<h3>Team Pitching {{ isRegularSeason ? '' : ' - Postseason' }}</h3>
<div class="table-responsive-xl">
<table class="table table-sm table-striped" id="table-pitching-stats">
<thead class="thead-dark">
<tr>
<th>Player</th>
<th>W</th>
<th>L</th>
<th>W-L%</th>
<th>ERA</th>
<th>G</th>
<th>GS</th>
<th>SV</th>
<th>HD</th>
<th>BSV</th>
<th>IP</th>
<th>H</th>
<th>R</th>
<th>ER</th>
<th>HR</th>
<th>BB</th>
<th>SO</th>
<th>HBP</th>
<th>BK</th>
<th>WP</th>
<th>IR</th>
<th>IRS</th>
<th>WHIP</th>
<th>H/9</th>
<th>HR/9</th>
<th>BB/9</th>
<th>SO/9</th>
<th>SO/BB</th>
<!-- <th>Last G</th>
<th>Last G-2</th> -->
</tr>
</thead>
<tbody id="team-pitching-stats">
<tr v-for="stat in pitchingStats">
<td>
<RouterLink
:to="{ name: 'player', params: { seasonNumber: seasonNumber, playerName: stat.player.name } }">
{{ stat.player.name }}
</RouterLink>
</td>
<td>{{ stat.win }}</td>
<td>{{ stat.loss }}</td>
<td>{{ winPercentage(stat) }}</td>
<td>{{ stat.era.toFixed(2) }}</td>
<td>{{ stat.games }}</td>
<td>{{ stat.gs }}</td>
<td>{{ stat.save }}</td>
<td>{{ stat.hold }}</td>
<td>{{ stat.bsave }}</td>
<td>{{ outsToInnings(stat) }}</td>
<td>{{ stat.hits }}</td>
<td>{{ stat.run }}</td>
<td>{{ stat.e_run }}</td>
<td>{{ stat.hr }}</td>
<td>{{ stat.bb }}</td>
<td>{{ stat.so }}</td>
<td>{{ stat.hbp }}</td>
<td>{{ stat.balk }}</td>
<td>{{ stat.wp }}</td>
<td>{{ stat.ir }}</td>
<td>{{ stat.ir_sc }}</td>
<td>{{ stat.whip.toFixed(2) }}</td>
<td>{{ hitsPer9(stat) }}</td>
<td>{{ hrsPer9(stat) }}</td>
<td>{{ stat.bbPer9.toFixed(1) }}</td>
<td>{{ stat.kPer9.toFixed(1) }}</td>
<td>{{ stat.kPerBB.toFixed(1) }}</td>
</tr>
</tbody>
<tfoot>
<tr v-if="totalPitchingStat">
<th>Total</th>
<th>{{ totalPitchingStat.win }}</th>
<th>{{ totalPitchingStat.loss }}</th>
<th>{{ winPercentage(totalPitchingStat) }}</th>
<th>{{ totalPitchingStat.era.toFixed(2) }}</th>
<th>{{ totalPitchingStat.games }}</th>
<th>{{ totalPitchingStat.gs }}</th>
<th>{{ totalPitchingStat.save }}</th>
<th>{{ totalPitchingStat.hold }}</th>
<th>{{ totalPitchingStat.bsave }}</th>
<th>{{ outsToInnings(totalPitchingStat) }}</th>
<th>{{ totalPitchingStat.hits }}</th>
<th>{{ totalPitchingStat.run }}</th>
<th>{{ totalPitchingStat.e_run }}</th>
<th>{{ totalPitchingStat.hr }}</th>
<th>{{ totalPitchingStat.bb }}</th>
<th>{{ totalPitchingStat.so }}</th>
<th>{{ totalPitchingStat.hbp }}</th>
<th>{{ totalPitchingStat.balk }}</th>
<th>{{ totalPitchingStat.wp }}</th>
<th>{{ totalPitchingStat.ir }}</th>
<th>{{ totalPitchingStat.ir_sc }}</th>
<th>{{ totalPitchingStat.whip.toFixed(2) }}</th>
<th>{{ hitsPer9(totalPitchingStat) }}</th>
<th>{{ hrsPer9(totalPitchingStat) }}</th>
<th>{{ totalPitchingStat.bbPer9.toFixed(1) }}</th>
<th>{{ totalPitchingStat.kPer9.toFixed(1) }}</th>
<th>{{ totalPitchingStat.kPerBB.toFixed(1) }}</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</template>
<script lang="ts">
import { aggregatePitchingStats, fetchPitchingStatsBySeasonAndTeamId, type PitchingStat } from '@/services/pitchingStatsService'
import { outsToInnings, winPercentage, hitsPer9, hrsPer9 } from '@/services/utilities'
export default {
name: "TeamPitchingTable",
props: {
seasonNumber: { type: Number, required: true },
teamId: { type: Number, required: true },
isRegularSeason: { type: Boolean, required: true }
},
data() {
return {
pitchingStats: [] as PitchingStat[]
}
},
computed: {
totalPitchingStat(): PitchingStat | undefined {
if (this.pitchingStats.length > 0) {
return aggregatePitchingStats(this.pitchingStats)
}
return undefined
}
},
created() {
this.fetchData()
},
methods: {
async fetchData(): Promise<void> {
const unsortedPitchingStats: PitchingStat[] = await fetchPitchingStatsBySeasonAndTeamId(this.seasonNumber, this.teamId, this.isRegularSeason)
this.pitchingStats = unsortedPitchingStats.sort((s1, s2) => s2.outs - s1.outs)
},
outsToInnings(stat: PitchingStat): string {
return outsToInnings(stat)
},
winPercentage(stat: PitchingStat): string {
return winPercentage(stat)
},
hitsPer9(stat: PitchingStat): string {
return hitsPer9(stat)
},
hrsPer9(stat: PitchingStat): string {
return hrsPer9(stat)
}
}
}
</script>

View File

@ -7,7 +7,7 @@ export async function fetchGamesBySeasonAndTeamId(seasonNumber: number, teamId:
return []
}
const response = await fetch(`${SITE_URL}/api/v3/games?season=${seasonNumber}&team1_id=${teamId}&team2_id=${teamId}`)
const response = await fetch(`${SITE_URL}/api/v3/games?season=${seasonNumber}&team1_id=${teamId}&team2_id=${teamId}&season_type=regular`)
const gamesResponse: {
count: number

View File

@ -152,6 +152,19 @@ export async function fetchPitchingStatsBySeasonAndPlayerId(seasonNumber: number
return normalizePitchingStat(pitchingStatsResponse.stats[0])
}
export async function fetchPitchingStatsBySeasonAndTeamId(seasonNumber: number, teamId: number, isRegularSeason: boolean): Promise<PitchingStat[]> {
const response = await fetch(`${SITE_URL}/api/v3/plays/pitching?season=${seasonNumber}&team_id=${teamId}&group_by=playerteam&s_type=${isRegularSeason ? 'regular' : 'post'}`)
const pitchingStatsResponse: {
count: number
stats: PitchingStatRaw[]
} = await response.json()
if (pitchingStatsResponse.count === 0) return []
return pitchingStatsResponse.stats.map(normalizePitchingStat)
}
async function fetchLegacyPitchingStatsBySeasonAndPlayerId(seasonNumber: number, playerId: number, isRegularSeason: boolean): Promise<PitchingStat | undefined> {
const response = await fetch(`${SITE_URL}/api/v3/pitchingstats/totals?season=${seasonNumber}&player_id=${playerId}&s_type=${isRegularSeason ? 'regular' : 'post'}`)

View File

@ -44,3 +44,21 @@ export function woba(stat: { bb: number, hbp: number, hit: number, double: numbe
export function outsToInnings(stat: { outs: number }): string {
return (stat.outs / 3).toFixed(1)
}
export function winPercentage(stat: { win: number, loss: number }): string {
if (stat.win + stat.loss === 0) return '-'
return (stat.win / (stat.win + stat.loss)).toFixed(3)
}
export function hitsPer9(stat: { outs: number, hits: number }): string {
if (stat.outs === 0) return '-'
return (stat.hits * 27 / stat.outs).toFixed(1)
}
export function hrsPer9(stat: { outs: number, hr: number }): string {
if (stat.outs === 0) return '-'
return (stat.hr * 27 / stat.outs).toFixed(1)
}

View File

@ -157,6 +157,12 @@
<!-- Schedule -->
<TeamScheduleTable v-if="!isFreeAgentTeam && team" :season-number="seasonNumber" :team-id="team.id" />
<!-- Team Pitching -->
<TeamPitchingTable v-if="!isFreeAgentTeam && team" :is-regular-season="true" :season-number="seasonNumber"
:team-id="team.id" />
<TeamPitchingTable v-if="!isFreeAgentTeam && team" :is-regular-season="false" :season-number="seasonNumber"
:team-id="team.id" />
</div>
</template>
@ -168,11 +174,13 @@ import { fetchStandings, type TeamStanding } from '@/services/standingsService'
import { fetchTeam } from '@/services/teamsService'
import { fetchTransactionsByTeamAndWeek, type Transaction } from '@/services/transactionsService'
import TeamScheduleTable from '@/components/TeamScheduleTable.vue'
import TeamPitchingTable from '@/components/TeamPitchingTable.vue'
export default {
name: "TeamView",
components: {
TeamScheduleTable
TeamScheduleTable,
TeamPitchingTable
},
data() {
return {