> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/TracingInsights-Archive/Stats/llms.txt
> Use this file to discover all available pages before exploring further.

# Race Results

> Collecting race finishing positions using results.py

The `results.py` script fetches race results including finishing positions, points awarded, and race outcomes for each driver.

## Script Overview

Location: `~/workspace/source/results.py`

The script uses the `RaceResultsFetcher` class to:

1. Fetch race results for specific rounds
2. Handle rate limiting automatically
3. Save results to the appropriate race directory
4. Log all operations for debugging

## API Endpoint

```python theme={null}
url = f"https://api.jolpi.ca/ergast/f1/{season}/{round_num}/results.json"
```

## RaceResultsFetcher Class

### Initialization

```python theme={null}
class RaceResultsFetcher:
    def __init__(self, base_dir="."):
        self.base_dir = Path(base_dir)
        self.base_url = "https://api.jolpi.ca/ergast/f1"
        # Rate limits
        self.burst_limit = 4  # requests per second
        self.last_request_time = 0
```

### Rate-Limited Requests

The class implements adaptive rate limiting:

```python theme={null}
def make_request(self, url):
    """Make a request to the API with rate limiting"""
    # Ensure we don't exceed burst limit
    current_time = time.time()
    time_since_last_request = current_time - self.last_request_time

    if time_since_last_request < (1 / self.burst_limit):
        sleep_time = (1 / self.burst_limit) - time_since_last_request
        logger.debug(f"Rate limiting: sleeping for {sleep_time:.2f} seconds")
        time.sleep(sleep_time)

    logger.debug(f"Making request to: {url}")
    response = requests.get(url)
    self.last_request_time = time.time()

    if response.status_code == 429:
        logger.warning("Rate limit exceeded. Waiting 30 seconds before retrying.")
        time.sleep(30)
        return self.make_request(url)

    if response.status_code != 200:
        logger.error(
            f"Error fetching data: {response.status_code} - {response.text}"
        )
        return None

    return response.json()
```

### Fetching Race Information

```python theme={null}
def get_race_info(self, season, round_num):
    """Get race information for a specific season and round"""
    url = f"{self.base_url}/{season}/{round_num}.json"
    data = self.make_request(url)

    if not data:
        return None

    races = data.get("MRData", {}).get("RaceTable", {}).get("Races", [])
    if races:
        return races[0]
    return None
```

### Fetching Results

```python theme={null}
def get_race_results(self, season, round_num):
    """Get race results for a specific season and round"""
    url = f"{self.base_url}/{season}/{round_num}/results.json"
    return self.make_request(url)
```

### Main Fetch Method

```python theme={null}
def fetch_round(self, season, round_num):
    """Fetch race results for a specific season and round"""
    logger.info(f"Fetching results for season {season}, round {round_num}")

    # Create season directory
    season_dir = self.base_dir / str(season)
    os.makedirs(season_dir, exist_ok=True)

    # Get race info
    race_info = self.get_race_info(season, round_num)

    if not race_info:
        logger.warning(f"No race info found for season {season}, round {round_num}")
        return

    race_name = self.get_race_folder_name(race_info)

    # Create race directory
    race_dir = season_dir / race_name
    os.makedirs(race_dir, exist_ok=True)

    # Get race results
    race_results = self.get_race_results(season, round_num)

    if race_results:
        race_results_path = race_dir / "results.json"
        self.save_json(race_results, race_results_path)
    else:
        logger.warning(f"No results found for season {season}, round {round_num}")
```

## Output Structure

Stored at: `{year}/{race-name}/results.json`

```json theme={null}
{
  "MRData": {
    "series": "f1",
    "url": "https://api.jolpi.ca/ergast/f1/1950/5/results.json",
    "limit": "30",
    "offset": "0",
    "total": "14",
    "RaceTable": {
      "season": "1950",
      "round": "5",
      "Races": [
        {
          "season": "1950",
          "round": "5",
          "raceName": "Belgian Grand Prix",
          "Circuit": {
            "circuitId": "spa",
            "circuitName": "Circuit de Spa-Francorchamps",
            "Location": {
              "lat": "50.4372",
              "long": "5.97139",
              "locality": "Spa",
              "country": "Belgium"
            }
          },
          "date": "1950-06-18",
          "Results": [
            {
              "number": "10",
              "position": "1",
              "positionText": "1",
              "points": "8",
              "Driver": {
                "driverId": "fangio",
                "givenName": "Juan",
                "familyName": "Fangio",
                "dateOfBirth": "1911-06-24",
                "nationality": "Argentine"
              },
              "Constructor": {
                "constructorId": "alfa",
                "name": "Alfa Romeo",
                "nationality": "Swiss"
              },
              "grid": "2",
              "laps": "35",
              "status": "Finished",
              "Time": {
                "millis": "10046000",
                "time": "2:47"
              }
            }
          ]
        }
      ]
    }
  }
}
```

## Usage Example

### Basic Usage

```python theme={null}
from results import RaceResultsFetcher

fetcher = RaceResultsFetcher(base_dir=".")
fetcher.fetch_round(2024, 1)  # Fetch results for 2024 Bahrain GP
```

### Multiple Rounds

```python theme={null}
fetcher = RaceResultsFetcher(base_dir=".")

for round_num in range(1, 22):
    fetcher.fetch_round(2024, round_num)
    
logger.info("Race results fetching completed")
```

<Note>
  The script includes logging to both a file (`race_results_fetch.log`) and the console for easy monitoring.
</Note>

## Results Data Fields

Each result contains:

| Field          | Description                                  |
| -------------- | -------------------------------------------- |
| `number`       | Driver's car number                          |
| `position`     | Finishing position                           |
| `positionText` | Position as text (handles special cases)     |
| `points`       | Championship points awarded                  |
| `Driver`       | Driver information (name, nationality, etc.) |
| `Constructor`  | Team information                             |
| `grid`         | Starting grid position                       |
| `laps`         | Number of laps completed                     |
| `status`       | Finish status (Finished, Retired, etc.)      |
| `Time`         | Finishing time (winner) or gap to winner     |
| `FastestLap`   | Fastest lap information (when available)     |

<Warning>
  Not all fields are available for all races, especially historical data. Always check for the existence of fields before accessing them.
</Warning>

## Logging Configuration

The script uses structured logging:

```python theme={null}
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("race_results_fetch.log"),
        logging.StreamHandler()
    ]
)
```

## Related Scripts

<CardGroup cols={2}>
  <Card title="Events" icon="calendar" href="/data-collection/events">
    Must run first to create race directories
  </Card>

  <Card title="Qualifying" icon="stopwatch" href="/data-collection/qualifying">
    Fetch qualifying results next
  </Card>
</CardGroup>
