82 lines
2.2 KiB
JavaScript
82 lines
2.2 KiB
JavaScript
const express = require('express');
|
|
const { requireAuth } = require('../middleware/auth');
|
|
const { getSupabaseServiceClient } = require('../utils/supabase');
|
|
|
|
const router = express.Router();
|
|
|
|
// GET /joint-history — joint outcome history and phi coefficient
|
|
router.get('/joint-history', requireAuth, async (req, res) => {
|
|
const { player_a, stat_a, player_b, stat_b } = req.query;
|
|
|
|
// Block free tier
|
|
if (!req.user.tier || req.user.tier === 'free') {
|
|
return res.status(403).json({
|
|
error: 'Joint history requires Analyst or Desk tier',
|
|
upgrade_url: '/pricing',
|
|
});
|
|
}
|
|
|
|
if (!player_a || !stat_a || !player_b || !stat_b) {
|
|
return res.status(400).json({
|
|
error: 'Required query params: player_a, stat_a, player_b, stat_b',
|
|
});
|
|
}
|
|
|
|
try {
|
|
const supabase = getSupabaseServiceClient();
|
|
const { data, error } = await supabase
|
|
.from('joint_outcomes')
|
|
.select('*')
|
|
.eq('player_a', player_a)
|
|
.eq('stat_a', stat_a)
|
|
.eq('player_b', player_b)
|
|
.eq('stat_b', stat_b);
|
|
|
|
if (error) throw error;
|
|
|
|
if (!data || data.length === 0) {
|
|
return res.json({
|
|
player_a,
|
|
stat_a,
|
|
player_b,
|
|
stat_b,
|
|
sample_size: 0,
|
|
phi_coefficient: null,
|
|
outcomes: [],
|
|
});
|
|
}
|
|
|
|
// Calculate phi coefficient from joint outcomes
|
|
let both_hit = 0, a_only = 0, b_only = 0, neither = 0;
|
|
for (const row of data) {
|
|
if (row.a_hit && row.b_hit) both_hit++;
|
|
else if (row.a_hit && !row.b_hit) a_only++;
|
|
else if (!row.a_hit && row.b_hit) b_only++;
|
|
else neither++;
|
|
}
|
|
|
|
const n = data.length;
|
|
const num = (both_hit * neither) - (a_only * b_only);
|
|
const denom = Math.sqrt(
|
|
(both_hit + a_only) * (b_only + neither) *
|
|
(both_hit + b_only) * (a_only + neither)
|
|
);
|
|
const phi = denom === 0 ? 0 : num / denom;
|
|
|
|
res.json({
|
|
player_a,
|
|
stat_a,
|
|
player_b,
|
|
stat_b,
|
|
sample_size: n,
|
|
phi_coefficient: Math.round(phi * 1000) / 1000,
|
|
outcomes: data,
|
|
});
|
|
} catch (err) {
|
|
console.error('[props/joint-history]', err.message);
|
|
res.status(503).json({ error: 'Service temporarily unavailable' });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|