Skip to main content

driver_points.py

The driver_points.py script fetches driver championship standings after each race. It tracks points accumulated by drivers throughout the season.

Overview

This script:
  • Fetches driver standings from the Ergast API
  • Saves cumulative points after each round
  • Implements rate limiting and logging
  • Requires the events.json file to exist first

Functions

create_folder_if_not_exists()

driver_points.py
def create_folder_if_not_exists(folder_path):
    """Create folder if it doesn't exist"""
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)
        logger.info(f"Created folder: {folder_path}")

get_race_folder_name()

driver_points.py
def get_race_folder_name(race):
    """Convert race name to folder name format"""
    return race["raceName"].lower().replace(" ", "-")

fetch_with_rate_limit()

driver_points.py
def fetch_with_rate_limit(url):
    """Fetch data with rate limiting"""
    logger.info(f"Fetching: {url}")
    time.sleep(REQUEST_DELAY)  # Respect rate limit
    response = requests.get(url)
    
    if response.status_code == 429:
        logger.warning("Rate limit exceeded. Waiting 30 seconds before retrying...")
        time.sleep(30)
        return fetch_with_rate_limit(url)
    
    if response.status_code != 200:
        logger.error(f"Error fetching {url}: {response.status_code} - {response.text}")
        return None
    
    return response.json()

fetch_driver_standings()

driver_points.py
def fetch_driver_standings(season, round_num):
    """Fetch driver standings for a specific round in a season"""
    url = f"{BASE_URL}/{season}/{round_num}/driverstandings/"
    return fetch_with_rate_limit(url)

process_round()

Main function that processes a specific round.
driver_points.py
def process_round(season, round_num):
    """Process a specific round in a season"""
    logger.info(f"Processing season: {season}, round: {round_num}")
    
    # Create season folder
    season_folder = Path(f"{season}")
    create_folder_if_not_exists(season_folder)
    
    # Fetch events data for the season
    events_file = season_folder / "events.json"
    if not events_file.exists():
        logger.warning(f"Events file not found for season {season}. Skipping.")
        return
    
    with open(events_file, "r") as f:
        events_data = json.load(f)
    
    races = events_data.get("MRData", {}).get("RaceTable", {}).get("Races", [])
    if not races:
        logger.warning(f"No races found for season {season}. Skipping.")
        return
    
    # Find the specific race for the round
    race = None
    for r in races:
        if r.get("round") == str(round_num):
            race = r
            break
    
    if not race:
        logger.warning(f"Round {round_num} not found for season {season}. Skipping.")
        return
    
    race_folder_name = get_race_folder_name(race)
    race_folder = season_folder / race_folder_name
    create_folder_if_not_exists(race_folder)
    
    # Fetch round-level driver standings
    round_standings = fetch_driver_standings(season, round_num)
    if round_standings:
        with open(race_folder / "driverPoints.json", "w") as f:
            json.dump(round_standings, f, indent=2)
            logger.info(f"Saved round driver standings to {race_folder}/driverPoints.json")

Usage

Fetch Standings for a Round

# Fetch driver standings after Round 1 of 2024
process_round(2024, 1)

# Fetch standings after Round 10
process_round(2024, 10)

Command Line

python driver_points.py
You must run events.py first to create the events.json file. This script depends on it to determine race folder names.

API Endpoint

GET https://api.jolpi.ca/ergast/f1/{season}/{round}/driverstandings/

Output Structure

Standings are saved to: {year}/{race-slug}/driverPoints.json
{
  "MRData": {
    "StandingsTable": {
      "season": "2024",
      "round": "1",
      "StandingsLists": [
        {
          "season": "2024",
          "round": "1",
          "DriverStandings": [
            {
              "position": "1",
              "points": "25",
              "wins": "1",
              "Driver": {
                "driverId": "verstappen",
                "code": "VER",
                "givenName": "Max",
                "familyName": "Verstappen",
                "dateOfBirth": "1997-09-30",
                "nationality": "Dutch"
              },
              "Constructors": [
                {
                  "constructorId": "red_bull",
                  "name": "Red Bull"
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

Configuration

BASE_URL = "https://api.jolpi.ca/ergast/f1"
RATE_LIMIT_BURST = 2  # requests per second
RATE_LIMIT_SUSTAINED = 500  # requests per hour
REQUEST_DELAY = 1 / RATE_LIMIT_BURST  # seconds between requests

Logging

Log file: driver_standings_fetch.log

See Also

Build docs developers (and LLMs) love