WAGERBABE DOCS
All Stories
2-1-11-player-recent-form-endpointDoneEpic 2.1

Story 2.1.11: Player Recent Form Endpoint

Status: done

Tasks

  • Task 1: Create Recent Form API Endpoint (AC: #1, #2, #3)
    • 1.1: Create `server/app/api/v1/endpoints/odds/player_form.py`
    • 1.2: Implement `GET /api/v1/optic-odds/players/{player_id}/recent-form` route
    • 1.3: Add query parameters: `games` (default: 5, max: 10), `stat` (e.g., passing_yards)
    • 1.4: Validate `player_id` format and `stat` against allowed stat types
    • 1.5: Register endpoint in `server/app/api/v1/endpoints/odds/__init__.py` router
  • Task 2: Integrate Optic Odds Player Results API (AC: #2, #3)
    • 2.1: Create `server/app/services/player_form_service.py`
    • 2.2: Implement `fetch_player_recent_games(client, player_id, games)` method
    • 2.3: Call Optic Odds `/fixtures/player-results` via existing OpticOddsAPIClient
    • 2.4: Parse response to extract per-game stat values, dates, opponents, results
    • 2.5: Handle API errors (404 player not found, 500 API unavailable)
    • 2.6: Transform Optic Odds response to internal `RecentGame` model
  • Task 3: Implement Analysis Calculations (AC: #4, #5)
    • 3.1: Create `calculate_threshold_stats(values, threshold)` function
    • 3.2: Create `calculate_trend(values)` function
    • 3.3: Calculate `trend_confidence` based on change percentage
    • 3.4: Calculate `consistency` using coefficient of variation
    • 3.5: Extract `threshold` from current prop line (if available) or use stat default from STAT_THRESHOLDS
  • Task 4: Create Response Schema (AC: #3, #4, #5)
    • 4.1: Create `server/app/schemas/odds/player_form.py`
    • 4.2: Define `PlayerInfo` model (id, name, team, position)
    • 4.3: Define `RecentGame` model (game_date, opponent, value, result, fixture_id)
    • 4.4: Define `FormAnalysis` model (average, min_value, max_value, over_threshold_count, threshold, trend, trend_confidence, consistency)
    • 4.5: Define `PlayerRecentFormResponse` model combining all fields + `PlayerRecentFormMeta`
  • Task 5: Implement Redis Caching (AC: #6)
    • 5.1: Use cache key pattern: `player:form:{player_id}:{stat_type}:{games}`
    • 5.2: Set TTL to 1 hour (WARM tier - 3600 seconds)
    • 5.3: Integrate with existing `RedisCacheManager` via `CacheMiddleware`
    • 5.4: Add cache headers to response (`X-Cache: HIT/MISS`, `X-Cache-Age`)
    • 5.5: Log cache hit/miss for observability
  • Task 6: Create PlayerTrendBadge Frontend Component (AC: #7)
    • 6.1: Create `client/src/components/sportsbook/prop-builder/PlayerTrendBadge.tsx`
    • 6.2: Display trend indicator: TrendingUp (green), TrendingDown (red), Minus (gray) with color
    • 6.3: Show "X of Y over {line}" text (e.g., "3 of 5 over 280")
    • 6.4: Make component expandable to show recent games breakdown
    • 6.5: Add loading skeleton state for async data
  • Task 7: Integrate with Prop Builder (AC: #7)
    • 7.1: Create `client/src/components/sportsbook/prop-builder/hooks/use-player-recent-form.ts` hook
    • 7.2: Configure 12-hour stale time, 24-hour cache time (historical data)
    • 7.3: Integrate `PlayerTrendBadge` into `PlayerPropRow.tsx` player rows
    • 7.4: Pass playerId, statType, and currentLine to PlayerTrendBadge
    • 7.5: Add error boundary for failed trend fetches (show null, don't break UI)
  • Task 8: Write Tests (AC: #1-6)
    • 8.1: Unit tests for `calculate_threshold_stats()` - various game counts and thresholds
    • 8.2: Unit tests for `calculate_trend()` - up/down/stable scenarios
    • 8.3: Unit tests for `calculate_consistency()` - high/low/perfect consistency
    • 8.4: Schema validation tests for all Pydantic models
    • 8.5: Test edge cases: single value, empty values, exact threshold match
    • 8.6: Test cache key pattern format

Progress

Tasks8/8
Acceptance Criteria0
Total Tasks8