feat: add monthlyHistory[] per player via getMonthlyHistory + bulk fetch (#6)

Add getMonthlyHistory() to models/player for single-player use and
getAllMonthlyHistoriesFromDB() for bulk fetches (one query, grouped in
memory). Wire monthlyHistory into all player objects returned by
getPlayerDataFromDB and getAllRatingsFromDB. Bulk path pre-fetches in
one query to avoid N extra per-player queries.
This commit is contained in:
Samuel Enocsson
2026-05-21 13:41:20 +02:00
parent 19756b80e5
commit 3dcd3131a0
5 changed files with 161 additions and 42 deletions
+13 -4
View File
@@ -1,10 +1,10 @@
const { db } = require('../db');
const { getPlayerFromDB, getRoundHistoryFromDB, savePredictedRatingToDB, savePlayerToDB } = require('../models/player');
const { getPlayerFromDB, getRoundHistoryFromDB, savePredictedRatingToDB, savePlayerToDB, getMonthlyHistory, getAllMonthlyHistoriesFromDB } = require('../models/player');
const { fetchPlayerDataHTTP, parsePlayerData } = require('../scrapers/player-http');
const { calculatePredictedRating } = require('./rating-calculator');
const logger = require('../logger');
async function getPlayerDataFromDB(pdgaNumber) {
async function getPlayerDataFromDB(pdgaNumber, { includeMonthlyHistory = true } = {}) {
try {
const cachedPlayer = await getPlayerFromDB(pdgaNumber);
if (cachedPlayer) {
@@ -23,6 +23,11 @@ async function getPlayerDataFromDB(pdgaNumber) {
const resolvedPredicted = predictedRating > 0 ? predictedRating : null;
const resolvedStdDev = stdDev > 0 ? stdDev : null;
// Skip in bulk-fetch paths where caller supplies history via getAllMonthlyHistoriesFromDB
const monthlyHistory = includeMonthlyHistory
? await getMonthlyHistory(cachedPlayer.pdga_number)
: [];
return {
pdgaNumber: cachedPlayer.pdga_number,
name: cachedPlayer.name,
@@ -34,7 +39,7 @@ async function getPlayerDataFromDB(pdgaNumber) {
lastMonthRating: (rating != null && ratingChange != null) ? rating - ratingChange : null,
// gap between next predicted update and current rating (null when either is missing)
deltaPredicted: (resolvedPredicted != null && rating != null) ? resolvedPredicted - rating : null,
monthlyHistory: []
monthlyHistory
};
}
return null;
@@ -137,6 +142,9 @@ async function getAllRatingsFromDB(progressCallback = null) {
logger.info(`Loading ${allPlayers.length} players from database...`);
// Fetch all monthly histories in one query so the per-player loop doesn't add N extra queries
const monthlyHistoryMap = await getAllMonthlyHistoriesFromDB(12);
const ratings = [];
const total = allPlayers.length;
@@ -154,9 +162,10 @@ async function getAllRatingsFromDB(progressCallback = null) {
}
try {
const playerData = await getPlayerDataFromDB(pdgaNumber);
const playerData = await getPlayerDataFromDB(pdgaNumber, { includeMonthlyHistory: false });
if (playerData) {
playerData.monthlyHistory = monthlyHistoryMap.get(pdgaNumber) ?? [];
ratings.push(playerData);
}