2366660f5e
Line movement system: - Baseline capture on first odds fetch of the day - Movement detection >= 0.5 points with direction (up/down) - Sharp money heuristic (sharp_action/public_action/unknown) - GET /api/movements with player, stat_type, min_movement filters - Movements included in GET /api/odds/nba live responses Cascade detection system: - Scratch detection: player props disappear from 2+ books - Affected user lookup via scan_sessions + picks - Parlay re-grade without scratched legs - cascade_alerts created for affected users - GET /api/alerts (Analyst/Desk only), PATCH /api/alerts/:id/read Zero extra Odds API credits — all detection piggybacks on existing fetches. Migration 002: line_baselines, line_movements, cascade_alerts tables. 30 new tests, 188 total (161 Node.js + 27 Python), all passing. Phase 2 Core Product COMPLETE. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
40 lines
1.0 KiB
JavaScript
40 lines
1.0 KiB
JavaScript
const { getSupabaseServiceClient } = require('../utils/supabase');
|
|
|
|
async function getAlertsForUser(userId) {
|
|
const supabase = getSupabaseServiceClient();
|
|
|
|
const { data: alerts, error } = await supabase
|
|
.from('cascade_alerts')
|
|
.select('*')
|
|
.eq('user_id', userId)
|
|
.eq('is_read', false)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
|
|
const { count } = await supabase
|
|
.from('cascade_alerts')
|
|
.select('*', { count: 'exact', head: true })
|
|
.eq('user_id', userId)
|
|
.eq('is_read', false);
|
|
|
|
return { alerts: alerts || [], unread_count: count || (alerts || []).length };
|
|
}
|
|
|
|
async function markAlertRead(alertId, userId) {
|
|
const supabase = getSupabaseServiceClient();
|
|
|
|
const { data, error } = await supabase
|
|
.from('cascade_alerts')
|
|
.update({ is_read: true })
|
|
.eq('id', alertId)
|
|
.eq('user_id', userId)
|
|
.select('id, is_read')
|
|
.single();
|
|
|
|
if (error || !data) return null;
|
|
return data;
|
|
}
|
|
|
|
module.exports = { getAlertsForUser, markAlertRead };
|