Goal
Reduce the repeated web-layer scaffolding that has accumulated across the team analytics pages before adding another major visualization page.
The intent is to remove mechanical duplication while keeping baseball-specific analytics explicit and readable.
Why
The same page lifecycle is now repeated across Hits, Batting Strikeouts, Runs, Baserunners, Pitching, Run Differential, Comparison, and related views:
parse team / season / window
→ load available team-seasons
→ build selector context
→ handle empty database
→ resolve selected team
→ handle missing team
→ resolve selected season
→ handle missing season
→ rebuild navigation
→ load persisted records
→ run metric-specific analytics
→ build chart
→ build summary cards
→ render template
The chart builders also repeat a large amount of common Plotly setup, including raw-game traces, rolling-average traces, reference lines, axis configuration, ticks, margins, legends, and annotations.
Several Pydantic analytics models additionally repeat identical consistency validators such as:
- prior-window fields must either both be present or both be absent
- summary game count must match point count
This was manageable when the app had only a few pages. With 6–7 similar pages and Player analytics planned, continuing to copy the same scaffolding will make changes harder to review and easier to apply inconsistently.
Scope
Routes
Extract small shared helpers for genuinely mechanical behavior such as:
- loading available team-seasons
- resolving team / season selections
- common base template context
- empty and not-found handling
- navigation/query-parameter preservation
Keep route-specific analytics, missing-data behavior, league gating, and explanatory copy explicit.
Do not turn every metric into a configuration-driven generic route.
Charts
Identify the common structure used by standard per-game count charts and extract focused helpers for things such as:
- raw observation trace
- rolling-average trace
- season / MLB reference lines
- standard x-axis ticks
- shared Plotly layout and legend configuration
Keep genuinely different charts separate. In particular, normalized comparison, run differential, and pitching/rate-specific behavior should not be forced into an abstraction that obscures their semantics.
Schemas / validators
Consolidate identical validation logic with small reusable helpers or narrowly-scoped base behavior where it improves readability.
Do not collapse Hits, Strikeouts, Runs, Baserunners, Pitching, etc. into one generic analytics model. Their baseball semantics should remain explicit.
Design constraints
- Prefer small helper functions over a generic metric framework
- Preserve thin routes
- Preserve pure analytics modules
- Preserve DB-only browser requests
- Preserve server-rendered FastAPI/Jinja
- Preserve local Plotly rendering
- Preserve current URLs and shareable query parameters
- Preserve all current empty, missing-data, coverage, and 409 re-import behavior
- Do not change statistical formulas or interpretation
- Do not change persistence or ingestion behavior as part of this issue unless a tiny supporting refactor is unavoidable
Testing
Existing behavior must remain unchanged.
Cover at minimum:
- Hits route regression
- Strikeouts route regression
- Runs route regression
- Baserunners route regression
- Pitching route regression
- Run Differential route regression
- Comparison route regression
- empty database behavior
- missing team / season behavior
- missing-data re-import states
- COMPLETE league-coverage gating
- query-parameter preservation
- chart trace names and reference lines
/health
- zero MLB API calls during browser rendering
Completion
- Common web-page plumbing is shared rather than copied across each metric route
- Standard count charts reuse a small, readable common chart foundation
- Repeated consistency validation is consolidated where appropriate
- Baseball-specific analytics remain explicit
- No generic metric framework or premature frontend architecture is introduced
- Existing tests remain green and behavior is unchanged
- Adding another straightforward team metric no longer requires copying an entire route and Plotly layout block
Context
This addresses duplication called out during the review of #38 and observed again as later analytics pages were added. PR #42 already consolidated duplicated batting/pitching upsert and game-log helper logic; this issue intentionally focuses on the remaining web-layer and analytics-schema duplication.
Goal
Reduce the repeated web-layer scaffolding that has accumulated across the team analytics pages before adding another major visualization page.
The intent is to remove mechanical duplication while keeping baseball-specific analytics explicit and readable.
Why
The same page lifecycle is now repeated across Hits, Batting Strikeouts, Runs, Baserunners, Pitching, Run Differential, Comparison, and related views:
The chart builders also repeat a large amount of common Plotly setup, including raw-game traces, rolling-average traces, reference lines, axis configuration, ticks, margins, legends, and annotations.
Several Pydantic analytics models additionally repeat identical consistency validators such as:
This was manageable when the app had only a few pages. With 6–7 similar pages and Player analytics planned, continuing to copy the same scaffolding will make changes harder to review and easier to apply inconsistently.
Scope
Routes
Extract small shared helpers for genuinely mechanical behavior such as:
Keep route-specific analytics, missing-data behavior, league gating, and explanatory copy explicit.
Do not turn every metric into a configuration-driven generic route.
Charts
Identify the common structure used by standard per-game count charts and extract focused helpers for things such as:
Keep genuinely different charts separate. In particular, normalized comparison, run differential, and pitching/rate-specific behavior should not be forced into an abstraction that obscures their semantics.
Schemas / validators
Consolidate identical validation logic with small reusable helpers or narrowly-scoped base behavior where it improves readability.
Do not collapse Hits, Strikeouts, Runs, Baserunners, Pitching, etc. into one generic analytics model. Their baseball semantics should remain explicit.
Design constraints
Testing
Existing behavior must remain unchanged.
Cover at minimum:
/healthCompletion
Context
This addresses duplication called out during the review of #38 and observed again as later analytics pages were added. PR #42 already consolidated duplicated batting/pitching upsert and game-log helper logic; this issue intentionally focuses on the remaining web-layer and analytics-schema duplication.