Sessions 5-7a: 955 tests, deployment ready
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
const axios = require('axios');
|
||||
|
||||
const EVOLUTION_SERVICE_URL = process.env.EVOLUTION_SERVICE_URL || 'http://localhost:5001';
|
||||
const EVOLUTION_TIMEOUT = 5000;
|
||||
|
||||
const TRACKED_SIGNALS = [
|
||||
'usage_rate',
|
||||
'assist_rate',
|
||||
'three_pt_attempt_rate',
|
||||
'shot_location',
|
||||
'aggression_score',
|
||||
'minutes_trajectory',
|
||||
];
|
||||
|
||||
/**
|
||||
* Detect changepoints in a player metric time series via Python PELT microservice.
|
||||
* @param {string} playerId
|
||||
* @param {string} metric - Metric name
|
||||
* @param {Array<number>} values - Time series values
|
||||
* @param {Array<string>} timestamps - ISO timestamps
|
||||
* @returns {object} Changepoint result or graceful degradation
|
||||
*/
|
||||
async function detectChangepoints(playerId, metric, values, timestamps) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${EVOLUTION_SERVICE_URL}/detect`,
|
||||
{ player_id: playerId, metric, values, timestamps },
|
||||
{ timeout: EVOLUTION_TIMEOUT }
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
const reason = error.code === 'ECONNABORTED'
|
||||
? 'timeout'
|
||||
: error.response
|
||||
? `HTTP ${error.response.status}`
|
||||
: error.message;
|
||||
return {
|
||||
evolution_detected: false,
|
||||
error: reason,
|
||||
playerId,
|
||||
metric,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if multiple signals are inflecting simultaneously.
|
||||
* If 2+ signals inflecting above 70% confidence, evolution is detected.
|
||||
* @param {object} playerMetrics - { signal_name: { values, timestamps, confidence } }
|
||||
* @returns {object} { evolution_detected, confidence, signals }
|
||||
*/
|
||||
async function checkMultiSignalEvolution(playerMetrics) {
|
||||
const results = [];
|
||||
|
||||
for (const signal of TRACKED_SIGNALS) {
|
||||
if (playerMetrics[signal]) {
|
||||
const { values, timestamps } = playerMetrics[signal];
|
||||
const result = await detectChangepoints(
|
||||
playerMetrics.playerId,
|
||||
signal,
|
||||
values,
|
||||
timestamps
|
||||
);
|
||||
if (result && !result.error) {
|
||||
results.push({ signal, ...result });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const inflecting = results.filter(r => r.confidence >= 0.70);
|
||||
|
||||
if (inflecting.length >= 2) {
|
||||
const avgConfidence = inflecting.reduce((s, r) => s + r.confidence, 0) / inflecting.length;
|
||||
return {
|
||||
evolution_detected: true,
|
||||
confidence: Math.round(avgConfidence * 100) / 100,
|
||||
signals: inflecting.map(r => r.signal),
|
||||
details: inflecting,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
evolution_detected: false,
|
||||
confidence: 0,
|
||||
signals: [],
|
||||
checked: TRACKED_SIGNALS.filter(s => playerMetrics[s]),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
EVOLUTION_SERVICE_URL,
|
||||
EVOLUTION_TIMEOUT,
|
||||
TRACKED_SIGNALS,
|
||||
detectChangepoints,
|
||||
checkMultiSignalEvolution,
|
||||
};
|
||||
Reference in New Issue
Block a user