chore: address review feedback on predicted rating logging (#11)

- align diagnostic endpoint date windows with getNextPDGAUpdateDate
- pass pdgaNumber to initial target-rating calculation for log attribution
- avoid double log when lazy-recompute returns 0
- log full error object in admin endpoint catch
This commit is contained in:
Samuel Enocsson
2026-05-25 22:02:42 +02:00
parent 83ba20d428
commit 31d80273b8
3 changed files with 14 additions and 10 deletions
+7 -6
View File
@@ -7,7 +7,7 @@ const { getOfficialRatingHistory, getOptimizedPlayerRounds } = require('../scrap
const { launchBrowser } = require('../scrapers/browser');
const { getPlayerDataFromDB, scrapePDGARating, getAllRatingsFromDB, refreshAllPlayersInDB, getPredictedRatingFromDB, formatDisplayDate } = require('../services/player-service');
const { getTopbarLocals } = require('../services/topbar-service');
const { calculatePredictedRating } = require('../services/rating-calculator');
const { calculatePredictedRating, getNextPDGAUpdateDate } = require('../services/rating-calculator');
const { calculateRequiredAverage } = require('../services/target-rating-calculator');
const logger = require('../logger');
@@ -449,9 +449,9 @@ router.get('/api/admin/player-state/:pdgaNumber', async (req, res) => {
return res.status(404).json({ error: 'Player not found', pdgaNumber: parseInt(pdgaNumber) });
}
const rounds = await getRoundHistoryFromDB(pdgaNumber);
const now = new Date();
const twelveMonthsAgo = new Date(now); twelveMonthsAgo.setFullYear(now.getFullYear() - 1);
const twentyFourMonthsAgo = new Date(now); twentyFourMonthsAgo.setFullYear(now.getFullYear() - 2);
const cutoff = getNextPDGAUpdateDate();
const twelveMonthsAgo = new Date(cutoff); twelveMonthsAgo.setFullYear(cutoff.getFullYear() - 1);
const twentyFourMonthsAgo = new Date(cutoff); twentyFourMonthsAgo.setFullYear(cutoff.getFullYear() - 2);
const roundsInLast12mo = rounds.filter(r => new Date(r.date) >= twelveMonthsAgo).length;
const roundsInLast24mo = rounds.filter(r => new Date(r.date) >= twentyFourMonthsAgo).length;
@@ -468,6 +468,7 @@ router.get('/api/admin/player-state/:pdgaNumber', async (req, res) => {
cutoffRating: player.cutoff_rating,
lastUpdated: player.last_updated,
lastRoundUpdate: player.last_round_update,
cutoffDate: cutoff.toISOString(),
roundCount: rounds.length,
roundsInLast12mo,
roundsInLast24mo,
@@ -475,7 +476,7 @@ router.get('/api/admin/player-state/:pdgaNumber', async (req, res) => {
newestRound: dates[dates.length - 1] ?? null
});
} catch (err) {
logger.error({ err: err.message, pdgaNumber }, 'admin player-state endpoint failed');
logger.error({ err, pdgaNumber }, 'admin player-state endpoint failed');
res.status(500).json({ error: 'Failed to fetch player state', details: err.message });
}
});
@@ -519,7 +520,7 @@ router.post('/api/calculate-target-rating/:pdgaNumber', async (req, res) => {
competition: r.competition_name
}));
const result = calculateRequiredAverage(roundRatings, target, numRounds);
const result = calculateRequiredAverage(roundRatings, target, numRounds, pdgaNum);
logger.info(`Target rating calc for PDGA ${pdgaNum}: target=${target} rounds=${numRounds} -> avg=${result.requiredAverage}`);
+5 -2
View File
@@ -42,7 +42,9 @@ async function getPlayerDataFromDB(pdgaNumber, { includeMonthlyHistory = true }
let stdDev = cachedPlayer.std_dev;
let excludedRoundsCount = cachedPlayer.excluded_rounds_count;
let cutoffRating = cachedPlayer.cutoff_rating;
let recomputeAttempted = false;
if (!predictedRating || predictedRating === 0) {
recomputeAttempted = true;
logger.debug({ pdgaNumber, dbValue: cachedPlayer.predicted_rating }, 'lazy recompute triggered for predicted_rating');
predictedRating = await getPredictedRatingFromDB(pdgaNumber);
const updatedPlayer = await getPlayerFromDB(pdgaNumber);
@@ -56,8 +58,9 @@ async function getPlayerDataFromDB(pdgaNumber, { includeMonthlyHistory = true }
const rating = cachedPlayer.current_rating;
const rawRatingChange = cachedPlayer.rating_change;
if (predictedRating != null && predictedRating <= 0) {
logger.warn({ pdgaNumber, dbValue: predictedRating }, 'predicted rating present but <= 0 — rendered as empty');
// Only warn about ≤0 if it wasn't already explained by a failed recompute (which has its own log)
if (!recomputeAttempted && predictedRating != null && predictedRating <= 0) {
logger.warn({ pdgaNumber, dbValue: predictedRating }, 'predicted rating present but <= 0 in DB without recompute — rendered as empty');
}
const resolvedPredicted = predictedRating > 0 ? predictedRating : null;
const resolvedStdDev = stdDev > 0 ? stdDev : null;
+2 -2
View File
@@ -1,14 +1,14 @@
const { calculatePredictedRating, getNextPDGAUpdateDate } = require('./rating-calculator');
const logger = require('../logger');
function calculateRequiredAverage(roundRatings, targetRating, numRounds) {
function calculateRequiredAverage(roundRatings, targetRating, numRounds, pdgaNumber) {
if (!Array.isArray(roundRatings) || roundRatings.length === 0) {
const err = new Error('No round history');
err.code = 'NO_ROUNDS';
throw err;
}
const currentPredicted = calculatePredictedRating(roundRatings, { callsite: 'target-rating-calculator' }).rating;
const currentPredicted = calculatePredictedRating(roundRatings, { pdgaNumber, callsite: 'target-rating-calculator' }).rating;
const nextUpdate = getNextPDGAUpdateDate();
const syntheticDate = new Date(nextUpdate.getTime() - 24 * 60 * 60 * 1000);