Session 43: Data pipeline + audit fixes + depth chart foundation (2045 tests)
P0 fixes + wiring real data into the S42 Player Intelligence architecture. - P0 dropdown z-index: the nav's backdrop-filter stacking context let the Ticker/HeartbeatBar paint over the avatar/More dropdowns and eat clicks. nav now position:relative zIndex:2; menus zIndex:100. Avatar Settings -> /settings. - Real MLB stats: mlbStatsAdapter.searchPlayer + getPlayerStats (name->id-> season+gamelog). playerIntelService.resolvePlayerStats normalizes into the archetype classifier; getPlayerIntel returns found:true + real season + archetype classified from real stats. NBA via nbaStatsClient (degrades). - Game cards: slateAdapter.groupPropsByPlayer (playerStrips, name once) + mapPitchers (MLB probables), folded into mapScheduleToGameCards. Legacy GameCard line grid renders BookChip (brand colors) not grey text. - Grade card intel: analyzeViaEngine1.buildIntelFields computes stat-context + form/usage/matchup/rest from the existing feature vector (zero extra I/O); gradeAdapter lights up the card sections. Archetype deferred (needs season line at grade time). - Depth chart foundation: depthChartService (getLineup/getDepthChart/ getCascadeProjection) + /api/stats/lineup|depth|cascade, graceful + injectable. - Mobile: player hero name overflow-wrap + 24px on <=640px (was clipping). Backend 2011 -> 2045 tests (+34), 163 suites. Web build clean (exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -129,10 +129,71 @@ async function getBatterVsPitcher(batterId, pitcherId, group = 'hitting') {
|
||||
return splits.length > 0 ? (splits[0].stat || null) : null;
|
||||
}
|
||||
|
||||
const normName = (n) => String(n == null ? '' : n).toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
|
||||
/**
|
||||
* Resolve a player name → MLB person record (Session 43). Pulls the season
|
||||
* player list (cached 24h — heavy but rarely changes) and matches by
|
||||
* normalized full name. Returns { id, fullName, team, teamId, position } or
|
||||
* null. Needed because every other adapter method keys on playerId.
|
||||
*/
|
||||
async function searchPlayer(name, season = DEFAULT_SEASON) {
|
||||
const target = normName(name);
|
||||
if (!target) return null;
|
||||
const url = `${BASE}/sports/1/players?season=${season}`;
|
||||
const data = await fetchWithCache(url, `mlbstats:players:${season}`, 24 * 3600);
|
||||
const people = (data && Array.isArray(data.people)) ? data.people : [];
|
||||
const hit = people.find((p) => normName(p.fullName) === target)
|
||||
|| people.find((p) => normName(p.fullName).includes(target) && target.length >= 6);
|
||||
if (!hit) return null;
|
||||
return {
|
||||
id: hit.id,
|
||||
fullName: hit.fullName ?? name,
|
||||
team: hit.currentTeam?.name ?? null,
|
||||
teamId: hit.currentTeam?.id ?? null,
|
||||
position: hit.primaryPosition?.abbreviation ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Name-keyed convenience: resolve the player, then fetch the season stat
|
||||
* object for the right group (pitching for pitchers, hitting otherwise) plus a
|
||||
* recent game log. Returns { found, id, name, team, position, group, season,
|
||||
* last10 } — `season` is the raw MLB stat object, mapped by the caller. Returns
|
||||
* { found: false } on any miss/failure (never throws).
|
||||
*/
|
||||
async function getPlayerStats(name, season = DEFAULT_SEASON) {
|
||||
try {
|
||||
const person = await searchPlayer(name, season);
|
||||
if (!person) return { found: false };
|
||||
const group = person.position === 'P' ? 'pitching' : 'hitting';
|
||||
const [seasonStat, log] = await Promise.all([
|
||||
getSeasonAverages(person.id, season, group),
|
||||
getPlayerGameLog(person.id, season, group),
|
||||
]);
|
||||
if (!seasonStat) return { found: false, id: person.id, name: person.fullName, team: person.team, position: person.position, group };
|
||||
return {
|
||||
found: true,
|
||||
id: person.id,
|
||||
name: person.fullName,
|
||||
team: person.team,
|
||||
position: person.position,
|
||||
group,
|
||||
season: seasonStat,
|
||||
last10: (log || []).slice(-10),
|
||||
};
|
||||
} catch (err) {
|
||||
console.warn('[mlbStats] getPlayerStats failed:', name, err.message);
|
||||
return { found: false };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getScheduleWithPitchers,
|
||||
getPlayerGameLog,
|
||||
getSeasonAverages,
|
||||
getBatterVsPitcher,
|
||||
__internals: { BASE, TTL, extractSplits, ymd, DEFAULT_SEASON },
|
||||
searchPlayer,
|
||||
getPlayerStats,
|
||||
__internals: { BASE, TTL, extractSplits, ymd, DEFAULT_SEASON, normName },
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user