McFARLAND:用 OpenAI 把棒球数据变成可分享解读的应用
rpollack/mcfarland
McFARLAND 是一个棒球分析应用,将当季球员数据与加权基线对比,生成可读、可分享的解读,并支持并排比较两三名打者或投手。它由 TypeScript monorepo 构建,前端用 React 18 + Vite,后端用 Node.js + Express,通过 OpenAI chat completions 生成标题与分析,默认模型为 gpt-5.4-mini。
Machine-crafted Forecasting And Reasoning for Luck, Analytics, Narratives, and Data
McFARLAND is a baseball analysis app that turns current-season performance data into readable, shareable takes. It is built for the space between a spreadsheet and a group chat: enough statistical rigor to be useful, but written in plain English and customizable voices.
The app compares a player's current-year stats against his weighted baseline, highlights what has actually changed, and asks the question fans and fantasy players usually care about: should this get better, worse, or stay about the same?
About The Project
McFARLAND exists because advanced baseball stats are useful, but they are often trapped in tables, acronyms, and context that casual fans do not want to decode every morning. The app takes the numbers that matter, filters out noise where it can, and turns them into short baseball arguments.
What it does:
- Builds single-player hitter and pitcher breakdowns using current-year stats, weighted baselines, skill indicators, luck indicators, and sample-size context.
- Compares two or three hitters or pitchers side by side and recommends the stronger profile.
- Lets users choose an analysis "vibe" so the same data can sound straightforward, analytical, fantasy-focused, Gen Z, Shakespearean, sensationalist, optimistic, old-school, or cranky old-timer.
- Surfaces quick-link player groups like breakouts, on-fire players, ice-cold players, and out-of-character profiles.
- Provides weekly trend views based on stored daily trend snapshots.
- Includes an admin-only social assistant that suggests timely player links and draft posts for X and Bluesky.
- Produces share-friendly URLs and dynamic Open Graph/Twitter metadata for player and comparison links.
The name is a tribute to former Orioles lefty T.J. McFarland, patron saint of adapting to the situation and doing useful work without needing the spotlight.
Current Tech Stack
McFARLAND is a TypeScript monorepo with three npm workspaces:
client: React 18, Vite, React Router, TanStack React Query, React Markdown, CSS modules, Vitest, Testing Library.server: Node.js, Express, TypeScript, Zod validation, Helmet, CORS, rate limiting, OpenAI chat completions, PostgreSQL viapg, CSV parsing, Vitest, Supertest.tests/e2e: Playwright end-to-end tests.
The production server builds the React app and serves it from the Express API. Development runs Vite and the API side by side.
Project Structure
.
├── client/ # React + Vite UI
├── server/ # Express API and analysis services
├── tests/e2e/ # Playwright tests
├── full_stats_*.csv # Current hitter/pitcher stat inputs
├── player_lookup.csv # Player lookup metadata
├── data_freshness.json # Current dataThroughDate metadata
├── refresh_data.R # Data refresh script
└── README.md
Core Runtime Behavior
- The server loads baseball CSV data at startup from the repository root.
/api/players,/api/players/:playerId,/api/trending, and/api/data-freshnessserve structured data to the client./api/analyzeand/api/compare/analyzebuild prompts from the current stats and selected vibe, then use OpenAI for headline + analysis generation.- Successful analysis responses are cached in PostgreSQL by prompt hash, cache version, mode, and
dataThroughDate, so repeat requests across users can skip OpenAI. - Cache entries are valid only for the current
dataThroughDate; stale rows are pruned opportunistically after successful writes. - Analytics events are sent to Amplitude for sessions, analysis runs, and share actions. Admin-mode requests are excluded from analytics.
- Weekly trend storage uses PostgreSQL when
DATABASE_URLis configured, with fallback behavior when storage is unavailable.
Prerequisites
- Node.js 18+
- npm 9+
- Optional: OpenAI API key for generated analysis.
- Optional: PostgreSQL 17 or compatible Postgres database for shared analysis cache and weekly trend persistence.
- Optional: R runtime for running
refresh_data.Rlocally.
Getting Started
Install dependencies:
npm install
For local AI responses, set an OpenAI key:
export OPENAI_API_KEY="your-openai-key"
For Postgres-backed cache and trend storage, set:
export DATABASE_URL="postgres://..."
Start the development environment:
npm run dev
The client runs at http://localhost:5173 and the API runs at http://localhost:3000.
Run the production build locally:
npm run start
The Express server serves the built React app at http://localhost:3000.
Environment Variables
| Name | Purpose | Default |
|---|---|---|
OPENAI_API_KEY |
Enables OpenAI-generated headline and analysis responses. | unset |
OPENAI_MODEL |
Model used for OpenAI-generated headline and analysis responses. | gpt-5.4-mini |
DATABASE_URL |
Enables Postgres-backed analysis cache and weekly trend storage. | unset |
AMPLITUDE_API_KEY |
Sends analytics events to a specific Amplitude project. | built-in project key |
ADMIN_PASSWORD |
Enables admin mode protection for social assistant endpoints. | unset |
TRENDS_REFRESH_SECRET |
Required secret for /api/trends/refresh. |
unset |
TRENDS_TIMEZONE |
Timezone used for trend date calculations. | America/Chicago |
CLIENT_ORIGIN |
Comma-separated allowed CORS origins. | unset |
TRUST_PROXY |
Express trust proxy setting for deployed environments. | 1 |
PORT |
API/server port. | 3000 |
Testing
Run server and client unit/API tests:
npm run test:unit
Run all workspace tests:
npm run testRun Playwright e2e tests:
npm run test:e2e
Run the server build only:
npm run build --workspace server
Run the full production build:
npm run build
Data And Caching Notes
- The app's baseball data comes from the CSV files in the repository root plus
data_freshness.json. refresh_data.Ris the data refresh entrypoint and is intended to be run on a schedule during the season.- Analysis cache validity follows
dataThroughDate, not wall-clock date. - Only successful structured OpenAI responses are cached. API errors, unavailable responses, and malformed model responses are not cached.
- The analysis cache is shared across users and sessions when Postgres is configured.
- Without
DATABASE_URL, analysis still works, but shared cache and persisted weekly trend storage are disabled.
License
Educational and personal use only. Respect applicable baseball data and image terms.
来源:GitHub · github.com