@@ -46,7 +46,7 @@ module.exports = {
46
46
table . removeBorder ( ) ;
47
47
return await getScreenshotOfHTMLTables ( [ table ] ) ;
48
48
} ,
49
- hydrateProbable : async ( probable , statType ) => {
49
+ hydrateProbable : async ( probable , statType , season = ( new Date ( ) . getFullYear ( ) ) ) => {
50
50
const [ spot , savant , people ] = await Promise . all ( [
51
51
new Promise ( ( resolve , reject ) => {
52
52
if ( probable ) {
@@ -59,10 +59,10 @@ module.exports = {
59
59
}
60
60
reject ( new Error ( 'There was a problem getting the player spot.' ) ) ;
61
61
} ) ,
62
- mlbAPIUtil . savantPitchData ( probable ) ,
62
+ mlbAPIUtil . savantPitchData ( probable , season ) ,
63
63
new Promise ( ( resolve , reject ) => {
64
64
if ( probable ) {
65
- resolve ( mlbAPIUtil . pitcher ( probable , 3 , statType ) ) ;
65
+ resolve ( mlbAPIUtil . pitcher ( probable , 3 , statType , season ) ) ;
66
66
} else {
67
67
resolve ( undefined ) ;
68
68
}
@@ -79,7 +79,7 @@ module.exports = {
79
79
} ;
80
80
} ,
81
81
82
- hydrateHitter : async ( hitter , statType ) => {
82
+ hydrateHitter : async ( hitter , statType , season = new Date ( ) . getFullYear ( ) ) => {
83
83
const [ spot , stats ] = await Promise . all ( [
84
84
new Promise ( ( resolve , reject ) => {
85
85
if ( hitter ) {
@@ -94,7 +94,7 @@ module.exports = {
94
94
} ) ,
95
95
new Promise ( ( resolve , reject ) => {
96
96
if ( hitter ) {
97
- resolve ( mlbAPIUtil . hitter ( hitter , statType ) ) ;
97
+ resolve ( mlbAPIUtil . hitter ( hitter , statType , season ) ) ;
98
98
} else {
99
99
resolve ( undefined ) ;
100
100
}
@@ -285,21 +285,23 @@ module.exports = {
285
285
return ( await getScreenshotOfHTMLTables ( [ table ] ) ) ;
286
286
} ,
287
287
288
- getStatcastData : ( savantText ) => {
288
+ getStatcastData : ( savantText , season ) => {
289
289
const statcast = / s t a t c a s t : \[ (?< statcast > .+ ) ] ,/ . exec ( savantText ) ?. groups . statcast ;
290
290
const metricSummaries = / m e t r i c S u m m a r y S t a t s : { (?< metricSummaries > .+ ) } , / . exec ( savantText ) ?. groups . metricSummaries ;
291
291
if ( statcast ) {
292
292
try {
293
293
const statcastJSON = JSON . parse ( '[' + statcast + ']' ) ;
294
294
const metricSummaryJSON = JSON . parse ( '{' + metricSummaries + '}' ) ;
295
- const mostRecentStatcast = statcastJSON . findLast ( set => set . year != null ) ;
295
+ const matchingStatcast = season ? statcastJSON . find ( set => set . year === season ) : statcastJSON . findLast ( set => set . year != null ) ;
296
296
// object properties are not guaranteed to always be in the same order, so we need to find the most recent year of data
297
- const mostRecentMetricYear = Object . keys ( metricSummaryJSON )
298
- . map ( k => parseInt ( k ) )
299
- . sort ( ( a , b ) => {
300
- return a < b ? 1 : - 1 ;
301
- } ) [ 0 ] ;
302
- return { mostRecentStatcast, metricSummaryJSON, mostRecentMetricYear } ;
297
+ const matchingMetricYear = season
298
+ ? Object . keys ( metricSummaryJSON ) . find ( k => k === season . toString ( ) )
299
+ : Object . keys ( metricSummaryJSON )
300
+ . map ( k => parseInt ( k ) )
301
+ . sort ( ( a , b ) => {
302
+ return a < b ? 1 : - 1 ;
303
+ } ) [ 0 ] ;
304
+ return { matchingStatcast, metricSummaryJSON, matchingMetricYear } ;
303
305
} catch ( e ) {
304
306
console . error ( e ) ;
305
307
return { } ;
@@ -756,7 +758,7 @@ module.exports = {
756
758
return matchingPlayers ;
757
759
} ,
758
760
759
- getPitcherEmbed : ( pitcher , pitcherInfo , isLiveGame , description , statType = 'R' , savantMode = false ) => {
761
+ getPitcherEmbed : ( pitcher , pitcherInfo , isLiveGame , description , statType = 'R' , savantMode = false , season = undefined ) => {
760
762
const feed = liveFeed . init ( globalCache . values . game . currentLiveFeed ) ;
761
763
if ( isLiveGame ) {
762
764
const abbreviations = {
@@ -774,7 +776,7 @@ module.exports = {
774
776
. setDescription ( '### ' + ( pitcherInfo . handedness
775
777
? pitcherInfo . handedness + 'HP **'
776
778
: '**' ) + ( pitcher . fullName || 'TBD' ) +
777
- '** (' + abbreviation + `): ${ pitcherInfo . pitchingStats . yearOfStats || 'Latest' } ${ ( ( ) => {
779
+ '** (' + abbreviation + `): ${ season || pitcherInfo . pitchingStats . yearOfStats || 'Latest' } ${ ( ( ) => {
778
780
if ( savantMode ) {
779
781
return 'Percentile Rankings' ;
780
782
}
@@ -802,7 +804,7 @@ module.exports = {
802
804
const embed = new EmbedBuilder ( )
803
805
. setTitle ( ( pitcherInfo . handedness
804
806
? pitcherInfo . handedness + 'HP '
805
- : '' ) + pitcher . fullName + ` (${ globals . TEAMS . find ( t => t . id === pitcher . currentTeam . id ) . abbreviation } ): ${ pitcherInfo . pitchingStats . yearOfStats || 'Latest' } ${ ( ( ) => {
807
+ : '' ) + pitcher . fullName + ` (${ globals . TEAMS . find ( t => t . id === pitcher . currentTeam . id ) . abbreviation } ): ${ season || pitcherInfo . pitchingStats . yearOfStats || 'Latest' } ${ ( ( ) => {
806
808
if ( savantMode ) {
807
809
return 'Percentile Rankings' ;
808
810
}
@@ -831,7 +833,7 @@ module.exports = {
831
833
}
832
834
} ,
833
835
834
- getBatterEmbed : ( batter , batterInfo , isLiveGame , description , statType = 'R' , savantMode = false ) => {
836
+ getBatterEmbed : ( batter , batterInfo , isLiveGame , description , statType = 'R' , savantMode = false , season = undefined ) => {
835
837
const feed = liveFeed . init ( globalCache . values . game . currentLiveFeed ) ;
836
838
let expandedBatter ;
837
839
if ( isLiveGame ) {
@@ -847,7 +849,7 @@ module.exports = {
847
849
const inning = feed . inning ( ) ;
848
850
const embed = new EmbedBuilder ( )
849
851
. setTitle ( halfInning . toUpperCase ( ) + ' ' + inning + ', ' +
850
- abbreviations . away + ' vs. ' + abbreviations . home + ': Current Batter' + ( savantMode ? ': Latest Percentile Rankings' : '' ) )
852
+ abbreviations . away + ' vs. ' + abbreviations . home + ': Current Batter' + ( savantMode ? `: ${ season || ' Latest' } Percentile Rankings` : '' ) )
851
853
. setDescription ( `### ${ batter . fullName } (${ abbreviation } )\n ${ expandedBatter . primaryPosition . abbreviation } | Bats ${ expandedBatter . batSide . description } ${ ( description || '' ) } ` )
852
854
. setImage ( 'attachment://savant.png' )
853
855
. setColor ( ( halfInning === 'top'
@@ -862,7 +864,7 @@ module.exports = {
862
864
return embed ;
863
865
} else {
864
866
const embed = new EmbedBuilder ( )
865
- . setTitle ( `${ batter . fullName } (${ globals . TEAMS . find ( team => team . id === batter . currentTeam . id ) . abbreviation } )` + ( savantMode ? ': Latest Percentile Rankings' : '' ) )
867
+ . setTitle ( `${ batter . fullName } (${ globals . TEAMS . find ( team => team . id === batter . currentTeam . id ) . abbreviation } )` + ( savantMode ? `: ${ season || ' Latest' } Percentile Rankings` : '' ) )
866
868
. setDescription ( `${ batter . primaryPosition . abbreviation } | Bats ${ batterInfo . stats . batSide . description } ` )
867
869
. setImage ( 'attachment://savant.png' )
868
870
. setColor ( globals . TEAMS . find ( team => team . id === batter . currentTeam . id ) . primaryColor ) ;
@@ -1144,20 +1146,23 @@ async function getScreenshotOfSavantTable (savantHTML) {
1144
1146
function buildSavantSection ( statCollection , metricSummaries , isPitcher = false ) {
1145
1147
const scale = chroma . scale ( [ '#325aa1' , '#a8c1c3' , '#c91f26' ] ) ;
1146
1148
const sliderScale = chroma . scale ( [ '#3661ad' , '#b4cfd1' , '#d8221f' ] ) ;
1147
- statCollection . forEach ( stat => {
1148
- if ( ! stat . percentile ) {
1149
- stat . percentile = calculateRoundedPercentileFromNormalDistribution (
1150
- stat . metric ,
1151
- stat . value ,
1152
- metricSummaries [ stat . metric ] . avg_metric ,
1153
- metricSummaries [ stat . metric ] . stddev_metric ,
1154
- stat . shouldInvert
1149
+ for ( let i = 0 ; i < statCollection . length ; i ++ ) {
1150
+ if ( ! statCollection [ i ] . value ) { // some metrics have been added in later years, like Bat Speed. Earlier seasons will have no value.
1151
+ continue ;
1152
+ }
1153
+ if ( ! statCollection [ i ] . percentile ) {
1154
+ statCollection [ i ] . percentile = calculateRoundedPercentileFromNormalDistribution (
1155
+ statCollection [ i ] . metric ,
1156
+ statCollection [ i ] . value ,
1157
+ metricSummaries [ statCollection [ i ] . metric ] ?. avg_metric ,
1158
+ metricSummaries [ statCollection [ i ] . metric ] ?. stddev_metric ,
1159
+ statCollection [ i ] . shouldInvert
1155
1160
) ;
1156
- stat . isQualified = false ;
1161
+ statCollection [ i ] . isQualified = false ;
1157
1162
} else {
1158
- stat . isQualified = true ;
1163
+ statCollection [ i ] . isQualified = true ;
1159
1164
}
1160
- } ) ;
1165
+ }
1161
1166
return statCollection . reduce ( ( acc , value ) => acc + ( value . value !== null
1162
1167
? `
1163
1168
<div class='savant-stat'>
0 commit comments