From d6ee77afaa8ea7418f26d31a11cba7a49a9ce505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Sun, 15 Jun 2025 22:28:17 +0200 Subject: [PATCH] start calculating stats --- .../migrations/15-extend-bullpen-session.js | 38 ++++++++++++ backend/models/bullpenSession.model.js | 20 +++++++ backend/seeders/05-bullpens.js | 59 ++++++++++++++++++- backend/test/data/bullpenSession.test.data.js | 5 ++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 backend/migrations/15-extend-bullpen-session.js diff --git a/backend/migrations/15-extend-bullpen-session.js b/backend/migrations/15-extend-bullpen-session.js new file mode 100644 index 0000000..cc4a573 --- /dev/null +++ b/backend/migrations/15-extend-bullpen-session.js @@ -0,0 +1,38 @@ +// Migration 2: Remove columns from Users +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + return Promise.all([ + queryInterface.addColumn('BullpenSessions','precisionRate', { + type: Sequelize.INTEGER, + allowNull: false + }), + queryInterface.addColumn('BullpenSessions','strikeRate', { + type: Sequelize.INTEGER, + allowNull: false + }), + queryInterface.addColumn('BullpenSessions','ballRate', { + type: Sequelize.INTEGER, + allowNull: false + }), + queryInterface.addColumn('BullpenSessions','fastballRate', { + type: Sequelize.INTEGER, + allowNull: false + }), + queryInterface.addColumn('BullpenSessions','offSpeedRate', { + type: Sequelize.INTEGER, + allowNull: false + }), + ]); + }, + + async down(queryInterface, Sequelize) { + return Promise.all([ + queryInterface.removeColumn('BullpenSessions', 'precisionRate'), + queryInterface.removeColumn('BullpenSessions', 'strikeRate'), + queryInterface.removeColumn('BullpenSessions', 'ballRate'), + queryInterface.removeColumn('BullpenSessions', 'fastballRate'), + queryInterface.removeColumn('BullpenSessions', 'offSpeedRate') + ]); + } +}; \ No newline at end of file diff --git a/backend/models/bullpenSession.model.js b/backend/models/bullpenSession.model.js index 6e61c8d..3c8fc13 100644 --- a/backend/models/bullpenSession.model.js +++ b/backend/models/bullpenSession.model.js @@ -25,6 +25,26 @@ module.exports = (sequelize, DataTypes) => { finishedAt: { type: DataTypes.DATE, allowNull: false, + }, + precisionRate: { + type: DataTypes.INTEGER, + allowNull: false, + }, + strikeRate: { + type: DataTypes.INTEGER, + allowNull: false, + }, + ballRate: { + type: DataTypes.INTEGER, + allowNull: false, + }, + fastballRate: { + type: DataTypes.INTEGER, + allowNull: false, + }, + offSpeedRate: { + type: DataTypes.INTEGER, + allowNull: false, } }, { sequelize, diff --git a/backend/seeders/05-bullpens.js b/backend/seeders/05-bullpens.js index 666ce68..8a291d3 100644 --- a/backend/seeders/05-bullpens.js +++ b/backend/seeders/05-bullpens.js @@ -10,6 +10,53 @@ const shouldMatch = (percentage) => { return Math.random() * 100 < percentage; } +const calculateRates = (pitches) => { + const bullpenPitches = pitches.reduce((acc, pitch) => { + const { bullpenSessionId } = pitch; + if (!acc[bullpenSessionId]) { + acc[bullpenSessionId] = []; + } + acc[bullpenSessionId].push(pitch); + return acc; + }, {}); + + const totalPitches = bullpenPitches.length; + if (totalPitches === 0) return { + precisionRate: 0, + strikeRate: 0, + ballRate: 0, + fastBallRate: 0, + offSpeedRate: 0 + }; + + const counts = bullpenPitches.reduce((acc, pitch) => { + // Check if it's a strike (hitArea 1-9) + const isStrike = pitch.hitArea >= 1 && pitch.hitArea <= 9; + + // Update counts based on conditions + acc.precision += pitch.aimedArea === pitch.hitArea ? 1 : 0; + acc.strikes += isStrike ? 1 : 0; + + if (isStrike) { + if (pitch.pitchTypeId === 1) { + acc.fastballs += 1; + } else { + acc.offspeed += 1; + } + } + + return acc; + }, { precision: 0, strikes: 0, fastballs: 0, offspeed: 0 }); + + return { + precisionRate: (counts.precision / totalPitches) * 100, + strikeRate: (counts.strikes / totalPitches) * 100, + ballRate: ((totalPitches - counts.strikes) / totalPitches) * 100, + fastBallRate: (counts.fastballs / totalPitches) * 100, + offSpeedRate: (counts.offspeed / totalPitches) * 100 + }; +} + const createBullpens = async (queryInterface, demoPlayer) => { const startDate = new Date(); // const endDate = new Date(new Date().setTime(startDate.getTime() + (10 * 60000))); @@ -22,7 +69,12 @@ const createBullpens = async (queryInterface, demoPlayer) => { startedAt: startedAt, finishedAt: new Date(new Date().setTime(startedAt.getTime() + (10 * 60000))), createdAt: new Date(), - updatedAt: new Date() + updatedAt: new Date(), + precisionRate: 0, + strikeRate: 0, + ballRate: 0, + fastBallRate: 0, + offSpeedRate: 0 } }) await queryInterface.bulkInsert('BullpenSessions', bullpens); @@ -64,6 +116,11 @@ const createBullpens = async (queryInterface, demoPlayer) => { }); await queryInterface.bulkInsert('Pitches', pitches.flat()); + + const rates = calculateRates(pitches); + + + await queryInterface.update(null, 'BullpenSessions', bullpens.map(bullpen => {})) } /** @type {import('sequelize-cli').Migration} */ diff --git a/backend/test/data/bullpenSession.test.data.js b/backend/test/data/bullpenSession.test.data.js index 07f7023..62bf7d7 100644 --- a/backend/test/data/bullpenSession.test.data.js +++ b/backend/test/data/bullpenSession.test.data.js @@ -2,6 +2,11 @@ const bullpenSession = { pitcherId: 1, startedAt: new Date(2025, 3, 22, 16, 5, 13), finishedAt: new Date(2025, 3, 22, 16, 23, 45), + precisionRate: 33, + strikeRate: 24, + ballRate: 76, + fastballRate: 55, + offSpeedRate: 78, pitches: [{ pitchTypeId: 1, pitchTime: new Date(2025, 3, 22, 16, 7, 21),