Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
c6ae935
Match React application shell to Streamlit reference
gmalbert Sep 30, 2026
1661fa1
Restyle React app to match Streamlit layout and theme
gmalbert Sep 30, 2026
f9542a8
Expose Streamlit shell metadata to React
gmalbert Sep 30, 2026
6087777
Support Streamlit-style table labels and checkbox cells
gmalbert Sep 30, 2026
3b9ec6e
Match Data Explorer controls and table to Streamlit
gmalbert Sep 30, 2026
45c00f5
Complete lightweight Analytics parity payload
gmalbert Sep 30, 2026
bd3954c
Render complete Streamlit Analytics section order
gmalbert Sep 30, 2026
bc89c19
Match Schedule page to Streamlit table and highlighting
gmalbert Sep 30, 2026
fd063f0
Match Next Race section order and presentation to Streamlit
gmalbert Sep 30, 2026
0b38550
Add Streamlit-style next-race artifact fallbacks
gmalbert Sep 30, 2026
7459e8d
Align Predictive Models controls and tabs with Streamlit
gmalbert Sep 30, 2026
d7d6671
Match Data and Debug tab structure to Streamlit
gmalbert Sep 30, 2026
6d103ab
Fix Streamlit analytics visual capture
gmalbert Sep 30, 2026
43ffe37
Run deployed Streamlit visual parity capture in CI
gmalbert Sep 30, 2026
4991cac
Update parity tests for Streamlit-matched UI
gmalbert Sep 30, 2026
524c08a
Update parity tests for Streamlit-matched UI
gmalbert Sep 30, 2026
f7c83fa
Update parity tests for Streamlit-matched UI
gmalbert Sep 30, 2026
d17c5b8
Update parity tests for Streamlit-matched UI
gmalbert Sep 30, 2026
4d9584c
Update parity tests for Streamlit-matched UI
gmalbert Sep 30, 2026
020c42d
Match Betting Oracle footer to Streamlit
gmalbert Sep 30, 2026
9a07744
Match footer spacing and typography to Streamlit
gmalbert Sep 30, 2026
4dd0c40
Match Betting Research controls and wording to Streamlit
gmalbert Sep 30, 2026
69a6516
Fix application shell accessibility lint
gmalbert Sep 30, 2026
3d754f4
Preserve keyboard-scrollable schedule with lint rationale
gmalbert Sep 30, 2026
705ba8d
Remove unused parity page imports
gmalbert Sep 30, 2026
1fe40cb
Remove unused parity page imports
gmalbert Sep 30, 2026
aa5b48c
Fix backend lint in parity metadata endpoint
gmalbert Sep 30, 2026
1ab1f7c
Format Analytics service imports for ruff
gmalbert Sep 30, 2026
73c38d7
Update Betting Research parity tests
gmalbert Sep 30, 2026
b4a3b7a
Fix Schedule JSX after accessibility cleanup
gmalbert Sep 30, 2026
f93ca18
Add Streamlit-equivalent multi-series and pie charts
gmalbert Sep 30, 2026
8990062
Match Streamlit driver trend and DNF pie visualizations
gmalbert Sep 30, 2026
4d0b08c
Use typed page descriptors in React shell
gmalbert Sep 30, 2026
6f1ef27
Fix model artifact table typing
gmalbert Sep 30, 2026
cb56a27
Type dynamic multi-series chart data
gmalbert Sep 30, 2026
b393d08
Match Streamlit simulation slider accessible label
gmalbert Sep 30, 2026
245f667
Port Streamlit safety-car artifact inference into FastAPI
gmalbert Sep 30, 2026
2fcefc3
Fix safety-car inference helper naming and imports
gmalbert Sep 30, 2026
653d533
Render Streamlit-equivalent safety-car probability table
gmalbert Sep 30, 2026
29c5338
Cover safety-car parity inference path
gmalbert Sep 30, 2026
6cde074
Resolve parity tooling dependencies from frontend package
gmalbert Sep 30, 2026
6b6704a
Resolve parity tooling dependencies from frontend package
gmalbert Sep 30, 2026
1c649b0
Resolve parity tooling dependencies from frontend package
gmalbert Sep 30, 2026
5b24a9a
Resolve parity tooling dependencies from frontend package
gmalbert Sep 30, 2026
6cac056
Resolve parity tooling dependencies from frontend package
gmalbert Sep 30, 2026
4230172
Fix backend lint blocker for parity CI
gmalbert Sep 30, 2026
3aeddcd
Add Streamlit analytics prediction tables from validation artifact
gmalbert Sep 30, 2026
b8c218a
Render Streamlit analytics prediction result tables
gmalbert Sep 30, 2026
3c29dbd
Complete Streamlit model diagnostics from committed artifacts
gmalbert Sep 30, 2026
379b4b2
Load all feature-selection parity artifacts
gmalbert Sep 30, 2026
c7352a2
Match Data Explorer inner Streamlit tabs
gmalbert Sep 30, 2026
3b79ead
Harden Streamlit visual capture for current Community Cloud DOM
gmalbert Sep 30, 2026
696c865
Enrich next-race prediction parity intervals and DNF stats
gmalbert Sep 30, 2026
e6fe7ab
Match Streamlit next-race position interval table
gmalbert Sep 30, 2026
9f0c04a
Add persistent Streamlit-style filter sidebar
gmalbert Sep 30, 2026
ffef9d9
Keep Streamlit filter sidebar visible across tabs
gmalbert Sep 30, 2026
b167394
Use global persistent filter sidebar on Data Explorer
gmalbert Sep 30, 2026
9466083
Style persistent filter sidebar like Streamlit
gmalbert Sep 30, 2026
f82ce26
Gate parity CI on accessibility and operational evidence
gmalbert Sep 30, 2026
4a9e4c5
Fix Models TypeScript inference after parity expansion
gmalbert Sep 30, 2026
5cb782c
Pin parity evidence upload action to immutable SHA
gmalbert Sep 30, 2026
d7e07eb
Update Data Explorer tests for persistent Streamlit sidebar
gmalbert Sep 30, 2026
a1ff416
Compare visual parity against local Streamlit reference checkout
gmalbert Sep 30, 2026
93cb886
Fix main navigation accessibility and exact page title
gmalbert Sep 30, 2026
f062f78
Fix tab and table landmark accessibility semantics
gmalbert Sep 30, 2026
832ae3c
Meet WCAG contrast while preserving Streamlit styling
gmalbert Sep 30, 2026
cc96aaf
Support mixed sort directions for Streamlit parity
gmalbert Sep 30, 2026
edebe4e
Match Streamlit year-desc position-asc result ordering
gmalbert Sep 30, 2026
b22bac5
Return complete Data Explorer results in Streamlit order
gmalbert Sep 30, 2026
fe36877
Use committed DNF model for next-race parity inference
gmalbert Sep 30, 2026
0a91a4f
Render artifact-backed next-race DNF predictions
gmalbert Sep 30, 2026
ae566cd
Add mobile viewport to visual parity capture
gmalbert Sep 30, 2026
66c528e
Add mobile viewport to visual parity capture
gmalbert Sep 30, 2026
70554a3
Enforce visual diff thresholds in CI
gmalbert Sep 30, 2026
859df05
Initialize Streamlit filters directly from loaded schema
gmalbert Sep 30, 2026
275ca7a
Update tests for accessible tab semantics
gmalbert Sep 30, 2026
48b6ab9
Update tests for accessible tab semantics
gmalbert Sep 30, 2026
558e384
Update tests for accessible tab semantics
gmalbert Sep 30, 2026
cc470bb
Update tests for accessible tab semantics
gmalbert Sep 30, 2026
a2821e4
Update app shell test for exact Streamlit title
gmalbert Sep 30, 2026
c669615
Remove obsolete Data Explorer filter helper
gmalbert Sep 30, 2026
b28434a
Update shared Tabs tests for ARIA tab role
gmalbert Sep 30, 2026
02b169e
Type mixed sort directions explicitly
gmalbert Sep 30, 2026
888c09c
Match constructor dominance wins and podiums chart
gmalbert Sep 30, 2026
50c952b
Render Streamlit constructor wins and podiums series
gmalbert Sep 30, 2026
8e39c22
Preserve Streamlit Data Debug marker
gmalbert Sep 30, 2026
04d7704
Expose saved-model DNF diagnostics for Next Race parity
gmalbert Sep 30, 2026
71a6744
Render Streamlit DNF diagnostic statistics
gmalbert Sep 30, 2026
c0082b3
Cancel superseded parity workflow runs
gmalbert Sep 30, 2026
60a2244
Fix keyboard access for scrollable tables
gmalbert Sep 30, 2026
d259a9c
Expand chart coverage tests
gmalbert Sep 30, 2026
5fc8351
Cover complete next-race rendering paths
gmalbert Sep 30, 2026
c56bdb8
Raise model page coverage across all advanced tabs
gmalbert Sep 30, 2026
477ad0a
Enforce 80 percent frontend line coverage
gmalbert Sep 30, 2026
e4e15c6
Always upload parity evidence artifacts
gmalbert Sep 30, 2026
6650d7b
Fix duplicate next-race text assertion
gmalbert Sep 30, 2026
4636d03
Match Streamlit shell spacing and responsive typography
gmalbert Sep 30, 2026
525828a
Keep parity evidence flowing through failed gates
gmalbert Sep 30, 2026
2d32182
Match Streamlit safety-car artifact loading
gmalbert Sep 30, 2026
7377ebb
Test safety-car artifact search parity
gmalbert Sep 30, 2026
789f85d
Match Streamlit loaded columns in filter schema
gmalbert Sep 30, 2026
c0e5ddd
Satisfy safety-car loader lint gate
gmalbert Sep 30, 2026
a2df5f9
Fix safety-car loader test typing import
gmalbert Sep 30, 2026
d2b3cc8
Stabilize missing-model async test
gmalbert Sep 30, 2026
953c1f6
Fix safety-car test fixture annotation
gmalbert Sep 30, 2026
fcb1da5
Align Streamlit shell and sidebar geometry
gmalbert Sep 30, 2026
d64742d
Match Streamlit range filter controls
gmalbert Sep 30, 2026
9ce0537
Preserve Streamlit filter column order
gmalbert Sep 30, 2026
880c289
Update filter slider test semantics
gmalbert Sep 30, 2026
05f57e1
Match Streamlit filter sidebar ordering
gmalbert Sep 30, 2026
41fa260
Match Streamlit default chart color
gmalbert Sep 30, 2026
89d5c71
Align Streamlit typography metrics
gmalbert Sep 30, 2026
860e98b
Normalize dynamic React parity evidence
gmalbert Sep 30, 2026
9f5c6c3
Normalize Streamlit parity evidence chrome
gmalbert Sep 30, 2026
e92ffee
Apply documented mobile visual tolerance
gmalbert Sep 30, 2026
f736e64
Match Streamlit canonical filter columns
gmalbert Oct 1, 2026
c5c0345
Keep Streamlit sidebar layout on narrow viewports
gmalbert Oct 1, 2026
37eaf0d
Mirror Streamlit standings enrichment for filters
gmalbert Oct 1, 2026
078db9d
Match Streamlit schedule column labels
gmalbert Oct 1, 2026
ccbab76
Match Streamlit next-race column labels
gmalbert Oct 1, 2026
1b385d4
Match Streamlit active-tab scrolling
gmalbert Oct 1, 2026
d44b2d8
Match Streamlit tab and form styling
gmalbert Oct 1, 2026
a4663ea
Test Streamlit standings-backed filter parity
gmalbert Oct 1, 2026
f553005
Match Streamlit dataframe header styling
gmalbert Oct 1, 2026
bf6655e
Match Streamlit subheader scale and spacing
gmalbert Oct 1, 2026
abe8268
Ignore one-pixel raster drift in visual diff
gmalbert Oct 1, 2026
ce3489b
Constrain sidebar controls to Streamlit content width
gmalbert Oct 1, 2026
f9dee9a
Preserve checkbox flex layout in sidebar
gmalbert Oct 1, 2026
8b2863e
Fix active-tab scroll implementation
gmalbert Oct 1, 2026
c8410a8
Remove literal newline escape from App source
gmalbert Oct 1, 2026
124c1a7
Match Streamlit checkbox alignment
gmalbert Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions .github/workflows/fastapi-react.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ name: FastAPI + React parity checks
permissions:
contents: read

concurrency:
group: fastapi-react-parity-${{ github.ref }}
cancel-in-progress: true

on:
pull_request:
paths:
Expand Down Expand Up @@ -70,3 +74,115 @@ jobs:
- name: Production-deps audit
run: npm run audit
continue-on-error: true # dev-dep advisories only; see PARITY_REPORT.md


visual-parity:
name: Visual parity evidence
runs-on: ubuntu-latest
needs: [backend, frontend]
timeout-minutes: 30
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"
cache: pip
cache-dependency-path: fastapi_react/backend/requirements.txt
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: "20"
cache: npm
cache-dependency-path: fastapi_react/frontend/package-lock.json
- name: Install backend
working-directory: fastapi_react/backend
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Install Streamlit reference runtime
run: pip install -r requirements-web.txt
- name: Install frontend and browser
working-directory: fastapi_react/frontend
run: |
npm ci
npx playwright install --with-deps chromium
- name: Start parity stack
env:
F1_REPO_ROOT: ${{ github.workspace }}
ENABLE_EXPENSIVE_TOOLS: "0"
F1_RESEARCH_MODE: "0"
STREAMLIT_SERVER_HEADLESS: "true"
run: |
(cd fastapi_react/backend && uvicorn app.main:app --host 127.0.0.1 --port 8000 > /tmp/f1-backend.log 2>&1 & echo $! > /tmp/f1-backend.pid)
(cd fastapi_react/frontend && npm run dev -- --host 127.0.0.1 --port 5173 > /tmp/f1-frontend.log 2>&1 & echo $! > /tmp/f1-frontend.pid)
(streamlit run raceAnalysis.py --server.address 127.0.0.1 --server.port 8501 --server.headless true > /tmp/f1-streamlit.log 2>&1 & echo $! > /tmp/f1-streamlit.pid)
for i in {1..90}; do
if curl -fsS http://127.0.0.1:8000/api/health >/dev/null && curl -fsS http://127.0.0.1:5173 >/dev/null && curl -fsS http://127.0.0.1:8501/_stcore/health >/dev/null; then
exit 0
fi
sleep 2
done
cat /tmp/f1-backend.log || true
cat /tmp/f1-frontend.log || true
cat /tmp/f1-streamlit.log || true
exit 1
- name: Capture React reference
working-directory: fastapi_react/frontend
env:
REACT_BASE_URL: http://127.0.0.1:5173
REACT_WAIT_MS: "4000"
run: npm run capture:react
- name: Accessibility audit
id: accessibility
continue-on-error: true
working-directory: fastapi_react/frontend
env:
REACT_BASE_URL: http://127.0.0.1:5173
run: npm run audit:a11y
- name: Operational benchmark
id: benchmark
continue-on-error: true
working-directory: fastapi_react/frontend
env:
BENCH_BASE_URL: http://127.0.0.1:5173
BENCH_API_URL: http://127.0.0.1:8000
run: npm run benchmark
- name: Capture local Streamlit reference
id: streamlit_capture
continue-on-error: true
working-directory: fastapi_react/frontend
env:
STREAMLIT_BASE_URL: http://127.0.0.1:8501
STREAMLIT_WAIT_MS: "5000"
run: npm run capture:streamlit
- name: Diff screenshots
id: visual_diff
continue-on-error: true
working-directory: fastapi_react/frontend
run: npm run capture:diff
- name: Upload visual parity evidence
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: fastapi-react-parity-evidence
path: |
fastapi_react/parity_evidence/visual
fastapi_react/parity_evidence/accessibility.json
fastapi_react/parity_evidence/benchmarks.json
if-no-files-found: warn
- name: Enforce parity evidence gates
if: always()
env:
ACCESSIBILITY_OUTCOME: ${{ steps.accessibility.outcome }}
BENCHMARK_OUTCOME: ${{ steps.benchmark.outcome }}
STREAMLIT_CAPTURE_OUTCOME: ${{ steps.streamlit_capture.outcome }}
VISUAL_DIFF_OUTCOME: ${{ steps.visual_diff.outcome }}
run: |
failed=0
for gate in ACCESSIBILITY_OUTCOME BENCHMARK_OUTCOME STREAMLIT_CAPTURE_OUTCOME VISUAL_DIFF_OUTCOME; do
outcome="${!gate}"
echo "$gate=$outcome"
if [ "$outcome" != "success" ]; then
failed=1
fi
done
exit "$failed"
16 changes: 16 additions & 0 deletions fastapi_react/backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import datetime
import os
from typing import Any

Expand All @@ -22,6 +23,7 @@
from app.services.data import (
filter_schema,
list_data_files,
load_main_data,
model_manifest,
precomputed,
query_main,
Expand Down Expand Up @@ -81,6 +83,16 @@ def brand_logo() -> FileResponse:

@app.get("/api/meta")
def meta() -> dict[str, Any]:
data = load_main_data()
years = data.get("grandPrixYear")
race_start_year = int(years.min()) if years is not None and years.notna().any() else 2016
current_year = int(years.max()) if years is not None and years.notna().any() else datetime.datetime.now().year
candidates = [path for path in DATA_DIR.iterdir() if path.is_file()] if DATA_DIR.exists() else []
latest = max(candidates, key=lambda path: path.stat().st_mtime, default=None)
last_updated = (
datetime.datetime.fromtimestamp(latest.stat().st_mtime).strftime("%Y-%m-%d %I:%M %p")
if latest is not None else "No data files found"
)
return {
"tabs": [
"Data Explorer", "Analytics", "Current Season", "Next Race",
Expand All @@ -89,6 +101,10 @@ def meta() -> dict[str, Any]:
"models": MODEL_TYPES,
"expensive_tools_enabled": ENABLE_EXPENSIVE_TOOLS,
"manual_tools": list(TOOLS),
"race_start_year": race_start_year,
"current_year": current_year,
"last_updated": last_updated,
"code_deployed_at": datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%d %H:%M:%S UTC"),
}


Expand Down
1 change: 1 addition & 0 deletions fastapi_react/backend/app/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class QueryRequest(BaseModel):
filters: list[FilterSpec] = Field(default_factory=list)
columns: list[str] | None = None
sort: list[str] = Field(default_factory=list)
ascending: list[bool] | None = None
descending: bool = False
offset: int = 0
limit: int = Field(default=200, ge=1, le=5000)
Expand Down
Loading
Loading