Backend REST API zwracający aktualne klasyfikacje, wyniki wyścigów i informacje o kierowcach Formuły 1. Asynchroniczne pobieranie danych z OpenF1 API, cache w PostgreSQL, walidacja Pydantic oraz 49 testów (42 jednostkowych + 7 integracyjnych).
API dostępne pod: https://f1-stats-g283.onrender.com
Interaktywna dokumentacja (Swagger UI): https://f1-stats-g283.onrender.com/docs
Aplikacja jest hostowana na darmowym planie Render — pierwsze wywołanie po dłuższej bezczynności może trwać ~30-50 sekund (cold start).
- Asynchroniczne REST API integrujące się z OpenF1 API
- Równoległe zapytania (asyncio.gather) z limitowaniem
- Walidacja danych przez Pydantic
- Cache w PostgreSQL z 3-poziomową logiką świeżości (no data / historical / current z TTL)
- 42 testy jednostkowe + 7 testów integracyjnych (TestClient + PostgreSQL w Dockerze)
- Mockowanie async (AsyncMock, side_effect, patch)
- Podział na warstwy (main / schemas / services / database)
- Konteneryzacja aplikacji (Docker + docker-compose)
- CI/CD (GitHub Actions — pytest, ruff, mypy)
- Migracja z file-based cache do PostgreSQL
- SQLAlchemy 2.0 z Mapped typed annotations
- Porównywarka kierowców (endpoint /compare/)
- Refactor wspólnej logiki get_driver_standings i get_constructor_standings (DRY)
- Redis — warstwa cache nad PostgreSQL
- Alembic migrations zamiast create_all
- Strukturalne logowanie (structlog)
- Middleware (request ID + timing)
- Rate limiting (slowapi)
- Paginacja + filtrowanie /drivers/
- Model użytkowników z rejestracją i logowaniem
- Autoryzacja JWT
- Prosty frontend konsumujący API
F1-Stats osiągnął wersję v1.0 — zaplanowany scope backendu został zrealizowany. Projekt służy jako data provider dla F1-Analyst — kolejnej warstwy z LLM analytics.
Dalsze rozszerzenia (Redis, JWT, frontend) są w Roadmap jako opcjonalne.
- Python 3.13
- FastAPI 0.136
- Pydantic 2.13
- SQLAlchemy 2.0 (async, Mapped)
- PostgreSQL 16
- httpx 0.28 (async)
- pytest 9.0, pytest-asyncio 1.3
- uvicorn 0.46
- OpenF1 API (zewnętrzne źródło danych)
- Render (deploy, Frankfurt EU)
- Docker + docker-compose (konteneryzacja)
- GitHub Actions (CI/CD)
- Asynchroniczne równoległe pobieranie ~25 sesji z OpenF1
- Cache w PostgreSQL z 3-poziomową logiką świeżości
- Rate limiting (Semaphore + sleep)
- Obsługa błędów HTTP (404 / 502 / 503) z fail-soft na pojedynczych rekordach
- Walidacja parametrów (Pydantic + Path z ge/le)
- 49 testów (pytest fixtures w conftest.py, AsyncMock, side_effect, TestClient dla integration)
- Clean architecture (separacja services/routers/schemas/database)
git clone https://github.com/Maksymilian03/F1-Stats
cd F1-Stats
python -m venv venv
# Windows
venv\Scripts\activate
# Mac/Linux
source venv/bin/activate
pip install -r requirements.txtWymagana baza PostgreSQL. Najprostszy setup przez Docker:
docker compose upAplikacja będzie dostępna pod http://localhost:8000. Dokumentacja Swagger pod http://localhost:8000/docs.
Aplikację można uruchomić w kontenerze Docker. Dostępne są dwa tryby:
docker build -t f1stats:latest .
docker run -p 8000:8000 f1stats:latestImage bazuje na python:3.13.2-slim. Aplikacja uruchamiana przez uvicorn bez --reload.
docker compose upKonfiguracja deweloperska z volumes (bind mount), --reload i PostgreSQL — zmiany w kodzie są od razu widoczne w kontenerze bez ponownego buildu.
Aplikacja będzie dostępna pod http://localhost:8000.
| Metoda | Endpoint | Opis |
|---|---|---|
| GET | / | Root endpoint |
| GET | /drivers/ | Lista aktualnych kierowców F1 |
| GET | /results/{year}/{country}/ | Wyniki konkretnego wyścigu |
| GET | /standings/{year}/ | Klasyfikacja kierowców |
| GET | /standings/constructors/{year}/ | Klasyfikacja konstruktorów |
| GET | /compare/{year}/{driver1_number}/{driver2_number}/ | Porównanie wyników 2 kierowców za dany sezon |
Parametr year przyjmuje wartości od 2023 do bieżącego roku.
[
{
"position": 1,
"full_name": "Max VERSTAPPEN",
"team": "Red Bull Racing",
"points": 434,
"wins": 9,
"driver_number": 1
},
{
"position": 2,
"full_name": "Lando NORRIS",
"team": "McLaren",
"points": 368,
"wins": 4,
"driver_number": 4
}
]Porównanie Verstappena (numer 1) i Hamiltona (numer 44) w sezonie 2023.
{
"driver1": {
"position": 1,
"full_name": "Max VERSTAPPEN",
"driver_number": 1,
"team": "Red Bull Racing",
"points": 566,
"wins": 19
},
"driver2": {
"position": 3,
"full_name": "Lewis HAMILTON",
"driver_number": 44,
"team": "Mercedes",
"points": 230,
"wins": 0
},
"comparison": {
"points_difference": 336,
"wins_difference": 19,
"position_difference": 2,
"leader": {
"position": 1,
"full_name": "Max VERSTAPPEN",
"driver_number": 1,
"team": "Red Bull Racing",
"points": 566,
"wins": 19
}
}
}Endpoint zwraca też błędy:
- 400 — gdy
driver1_number == driver2_number - 404 — gdy któryś z kierowców nie startował w danym sezonie
- 422 — gdy
year < 2023lubdriver_numberpoza zakresem 1-99
Aplikacja jest podzielona na warstwy:
main.py— Routery FastAPI + walidacja parametrów (Path)services.py— Logika biznesowa, integracja z OpenF1, cacheschemas.py— Modele Pydantic (response_model)database.py— SQLAlchemy async session, engine, get_dbmodels.py— Modele ORM (DriverStanding, ConstructorStanding)tests/— 49 testów:- logika obliczeń: calculate_points, aggregate_points_by_team, leaderboard, merge_driver_details
- integracja z OpenF1 (mock async): fetch_drivers, fetch_session_results, get_races_and_sprints
- endpoint orchestracji: get_driver_standings (happy path + cache hit + cache miss)
- integration tests dla get_driver_standings z PostgreSQL w Dockerze
conftest.py— fixtures z danymi testowymi i session DB
- Endpoint w
main.pywaliduje parametryear(Path zge=2023, le=CURRENT_YEAR) - FastAPI wstrzykuje async session przez
Depends(get_db) - Service
get_driver_standings(2024)sprawdza świeżość danych w PostgreSQL (3 przypadki: brak danych / historyczny rok / bieżący rok z TTL) - Cache hit: jedno SELECT, zwrot do klienta (~10 ms)
- Cache miss: funkcja pobiera listę sesji (~25: race + sprint) z OpenF1
- Równolegle (
asyncio.gather) pobiera wyniki każdej sesji z rate limitingiem (Semaphore(1)+ sleep) - Agregacja punktów per kierowca (czyste funkcje, łatwe do testowania)
- Merge z danymi kierowców (full_name, team_name)
- Sortowanie + dodanie pozycji (
leaderboard()z tie-breakerem po wins) - Zapis do PostgreSQL + Pydantic validation (
response_model=List[StandingsEntry]) - Zwrot do klienta w postaci JSON
Testy jednostkowe (bez bazy):
pytest tests/ -vTesty integracyjne (wymagają PostgreSQL w Dockerze):
docker compose up -d
pytest tests/integration/ -vWszystkie testy:
docker compose up -d
pytest -v