tracinginsights/api
1
1# from git import Repo2# import os3 4# GITHUB_PAT = os.environ['GITHUB']5 6# if not os.path.exists('repo_directory'): 7# # os.mkdir('repo_directory')8# Repo.clone_from(f'https://tracinginsights:{GITHUB_PAT}@github.com/TracingInsights/fastf1api.git', 'repo_directory' )9 10# from repo_directory.main import *11 12 13import concurrent.futures14import datetime15import functools16import math17import os18from io import BytesIO19 20import fastf121import numpy as np22import pandas as pd23import requests24import streamlit as st25from fastapi import Depends, FastAPI26from fastapi.middleware.cors import CORSMiddleware27from fastapi.responses import FileResponse, HTMLResponse28from fastf1.ergast import Ergast29from pydantic import BaseModel, Field30from sqlalchemy.orm import Session31 32# from . import accelerations, database, models, utils33 34import accelerations35import database36import models37import utils38FASTF1_CACHE_DIR = os.environ["FASTF1_CACHE_DIR"]39 40fastf1.Cache.enable_cache(FASTF1_CACHE_DIR)41 42app = FastAPI()43 44app.add_middleware(45 CORSMiddleware,46 allow_origins=["*"],47 allow_credentials=True,48 allow_methods=["*"],49 allow_headers=["*"],50)51 52 53database.Base.metadata.create_all(bind=database.engine)54 55 56def get_db():57 try:58 db = database.SessionLocal()59 yield db60 finally:61 db.close()62 63 64class RacePace(BaseModel):65 year: int66 event: str67 session: str68 Driver: str69 LapTime: float70 Diff: float71 Team: str72 fill: str73 74 75# @functools.cache76@app.get("/racepace/{year}/{event}/{session}", response_model=None)77async def average_race_pace(78 year: int, event: str | int, session: str, db: Session = Depends(get_db)79) -> any:80 race_pace_data = (81 db.query(models.RacePace)82 .filter_by(year=year, event=event, session=session)83 .all()84 )85 86 if race_pace_data:87 print("Fetching from Database")88 89 if not race_pace_data:90 print("Writing to Database")91 f1session = fastf1.get_session(92 year,93 event,94 session,95 # backend="fastf1",96 # force_ergast=False,97 )98 f1session.load(telemetry=False, weather=False, messages=False)99 laps = f1session.laps100 101 laps = laps.loc[laps.LapNumber > 1]102 laps = laps.pick_track_status(103 "1",104 )105 laps["LapTime"] = laps.Sector1Time + laps.Sector2Time + laps.Sector3Time106 107 # convert LapTime to seconds108 laps["LapTime"] = laps["LapTime"].apply(lambda x: x.total_seconds())109 110 laps = laps.loc[laps.LapTime < laps.LapTime.min() * 1.07]111 112 df = (113 laps[["LapTime", "Driver"]].groupby("Driver").mean().reset_index(drop=False)114 )115 df = df.sort_values(by="LapTime").reset_index(drop=True)116 df["LapTime"] = df["LapTime"].round(3)117 df["Diff"] = (df["LapTime"] - df["LapTime"].min()).round(3)118 teams = laps[["Driver", "Team"]].drop_duplicates().reset_index(drop=True)119 # join teams and df120 df = df.merge(teams, on="Driver", how="left")121 122 car_colors = utils.team_colors(year)123 124 df["fill"] = df["Team"].map(car_colors)125 126 df_json = df.to_dict("records")127 128 # save the data to the database129 for record in df.to_dict("records"):130 race_pace = models.RacePace(**record)131 db.add(race_pace)132 133 db.commit()134 135 return {"racePace": df_json}136 137 return {"racePace": [dict(race_pace) for race_pace in race_pace_data]}138 139 140@functools.cache141@app.get("/topspeed/{year}/{event}/{session}", response_model=None)142async def top_speed(year: int, event: str | int, session: str) -> any:143 f1session = fastf1.get_session(year, event, session)144 f1session.load(telemetry=False, weather=False, messages=False)145 laps = f1session.laps146 team_colors = utils.team_colors(year)147 148 fastest_speedtrap = (149 laps[["SpeedI1", "SpeedI2", "SpeedST", "SpeedFL"]]150 .idxmax(axis=1)151 .value_counts()152 .index[0]153 )154 155 speed_df = (156 laps[[fastest_speedtrap, "Driver", "Compound", "Team"]]157 .groupby("Driver")158 .max()159 .sort_values(fastest_speedtrap, ascending=False)160 .reset_index()161 )162 # add team colors to dataframe163 speed_df["fill"] = speed_df["Team"].apply(lambda x: team_colors[x])164 165 # rename fastest speedtrap column to TopSpeed166 speed_df.rename(columns={fastest_speedtrap: "TopSpeed"}, inplace=True)167 168 # remove nan values in any column169 speed_df = speed_df.dropna()170 171 # Convert to int172 speed_df["TopSpeed"] = speed_df["TopSpeed"].astype(int)173 174 speed_dict = speed_df.to_dict(orient="records")175 176 return {"topSpeed": speed_dict}177 178 179@functools.cache180@app.get("/overtakes/{year}/{event}", response_model=None)181def get_overtakes(year: int, event: str) -> any:182 def get_overtakes_df(year, event):183 if year == 2023:184 url = "https://docs.google.com/spreadsheets/d/1M4aepPJaIfdqE9oU3L-2CQqKIyubLXG4Q4cqWnyqxp4/export?format=csv"185 if year == 2022:186 url = "https://docs.google.com/spreadsheets/d/1cuS3B6hk4iQmMaRQoMTcogIInJpavnV7rKuEsiJnEbU/export?format=csv"187 if year == 2021:188 url = "https://docs.google.com/spreadsheets/d/1ANQnPVkefRmvzrmGvEqXoqQ4dBfgcI_R9FPg-0BcM34/export?format=csv"189 if year == 2020:190 url = "https://docs.google.com/spreadsheets/d/1eG9WTkXKzFT4NMh-WqHOMs5G0UuPGnb6wP4CnFD8uzY/export?format=csv"191 if year == 2019:192 url = "https://docs.google.com/spreadsheets/d/10nHg7BIs5ySh_dE9uuIz2lq-gRWcg02tIMr0EPgPvJs/export?format=csv"193 if year == 2018:194 url = "https://docs.google.com/spreadsheets/d/1MyAwQdczccdca_FAIiZKkqZNauNh3ts99JZ278S2OKc/export?format=csv"195 196 response = requests.get(url, timeout=10)197 df = pd.read_csv(BytesIO(response.content))198 df = df[["Driver", event]]199 # replace NaNs with 0s200 df = df.fillna(0)201 # convert numbers to ints202 df[event] = df[event].astype(int)203 # replace event with "overtakes"204 df = df.rename(columns={event: "overtakes"})205 return df206 207 def get_overtaken_df(year, event):208 if year == 2023:209 url = "https://docs.google.com/spreadsheets/d/1wszzx694Ot-mvA5YrFCpy3or37xMgnC0XpE8uNnJLWk/export?format=csv"210 if year == 2022:211 url = "https://docs.google.com/spreadsheets/d/19_XFDD3BZDIQVkNE4bG6dwuKvMaO4g5HNaUARGaJwhE/export?format=csv"212 if year == 2021:213 url = "https://docs.google.com/spreadsheets/d/1dQBHnd3AXEPNH5I75cjbzAAzi9ipqGk3v9eZT9eYKS4/export?format=csv"214 if year == 2020:215 url = "https://docs.google.com/spreadsheets/d/1snyntPMxYH4_KHSRI96AwBoJQrPbX6OanJAcqbYyW-Y/export?format=csv"216 if year == 2019:217 url = "https://docs.google.com/spreadsheets/d/11FfFkXErJg7F22iVwJo9XfLFAWucMBVlzL1qUGWxM3s/export?format=csv"218 if year == 2018:219 url = "https://docs.google.com/spreadsheets/d/1XJXAEyRpRS_UwLHzEtN2PdIaFJYGWSN6ypYN8Ecwp9A/export?format=csv"220 221 response = requests.get(url, timeout=10)222 df = pd.read_csv(BytesIO(response.content))223 df = df[["Driver", event]]224 # replace NaNs with 0s225 df = df.fillna(0)226 # convert numbers to ints227 df[event] = df[event].astype(int)228 df = df.rename(columns={event: "overtaken"})229 return df230 231 overtakes = get_overtakes_df(year, event)232 overtaken = get_overtaken_df(year, event)233 df = overtakes.merge(overtaken, on="Driver")234 235 # remove drivers with 0 overtakes and 0 overtaken236 df = df[(df["overtakes"] != 0) | (df["overtaken"] != 0)]237 238 # sort in the decreasing order of overtakes239 df = df.sort_values(240 by=["overtakes", "overtaken"], ascending=[False, True]241 ).reset_index(drop=True)242 # convert to dictionary243 df_dict = df.to_dict(orient="records")244 245 return {"overtakes": df_dict}246 247 248@functools.cache249@app.get("/fastest/{year}/{event}/{session}", response_model=None)250async def fastest_lap(year: int, event: str | int, session: str) -> any:251 f1session = fastf1.get_session(year, event, session)252 f1session.load(telemetry=False, weather=False, messages=False)253 laps = f1session.laps254 255 drivers = pd.unique(laps["Driver"])256 257 list_fastest_laps = list()258 259 for drv in drivers:260 drvs_fastest_lap = laps.pick_driver(drv).pick_fastest()261 list_fastest_laps.append(drvs_fastest_lap)262 263 df = (264 fastf1.core.Laps(list_fastest_laps)265 .sort_values(by="LapTime")266 .reset_index(drop=True)267 )268 269 pole_lap = df.pick_fastest()270 df["Diff"] = df["LapTime"] - pole_lap["LapTime"]271 272 car_colors = utils.team_colors(year)273 274 df["fill"] = df["Team"].map(car_colors)275 276 # convert timedelta to float and round to 3 decimal places277 df["Diff"] = df["Diff"].dt.total_seconds().round(3)278 df = df[["Driver", "LapTime", "Diff", "Team", "fill"]]279 280 # remove nan values in any column281 df = df.dropna()282 283 df_json = df.to_dict("records")284 285 return {"fastest": df_json}286 287 288# @st.cache_data289 290 291@app.get("/wdc", response_model=None)292async def driver_standings() -> any:293 YEAR = 2023 # datetime.datetime.now().year294 df = pd.DataFrame(295 pd.read_html(f"https://www.formula1.com/en/results.html/{YEAR}/drivers.html")[0]296 )297 df = df[["Driver", "PTS", "Car"]]298 # reverse the order299 df = df.sort_values(by="PTS", ascending=True)300 301 # in Driver column only keep the last 3 characters302 df["Driver"] = df["Driver"].str[:-5]303 304 # add colors to the dataframe305 car_colors = utils.team_colors(YEAR)306 df["fill"] = df["Car"].map(car_colors)307 308 # remove rows where points is 0309 df = df[df["PTS"] != 0]310 df.reset_index(inplace=True, drop=True)311 df.rename(columns={"PTS": "Points"}, inplace=True)312 313 return {"WDC": df.to_dict("records")}314 315 316# @st.cache_data317 318 319@app.get("/", response_model=None)320async def root():321 return HTMLResponse(322 content="""<iframe src="https://tracinginsights-f1-analysis.hf.space" frameborder="0" style="width:100%; height:100%;" scrolling="yes" allowfullscreen:"yes"></iframe>""",323 status_code=200,324 )325 326 327# @st.cache_data328 329 330@app.get("/years", response_model=None)331async def years_available() -> any:332 # make a list from 2018 to current year333 current_year = datetime.datetime.now().year334 years = list(range(2018, current_year + 1))335 # reverse the list to get the latest year first336 years.reverse()337 years = [{"label": str(year), "value": year} for year in years]338 return {"years": years}339 340 341# format for events {"events":[{"label":"Saudi Arabian Grand Prix","value":2},{"label":"Bahrain Grand Prix","value":1},{"label":"Pre-Season Testing","value":"t1"}]}342 343# @st.cache_data344 345 346@app.get("/{year}", response_model=None)347async def events_available(year: int) -> any:348 # get events available for a given year349 data = utils.LatestData(year)350 events = data.get_events()351 events = [{"label": event, "value": event} for i, event in enumerate(events)]352 events.reverse()353 354 return {"events": events}355 356 357# format for sessions {"sessions":[{"label":"FP1","value":"FP1"},{"label":"FP2","value":"FP2"},{"label":"FP3","value":"FP3"},{"label":"Qualifying","value":"Q"},{"label":"Race","value":"R"}]}358 359 360# @st.cache_data361@functools.cache362@app.get("/{year}/{event}", response_model=None)363async def sessions_available(year: int, event: str | int) -> any:364 # get sessions available for a given year and event365 data = utils.LatestData(year)366 sessions = data.get_sessions(event)367 sessions = [{"label": session, "value": session} for session in sessions]368 369 return {"sessions": sessions}370 371 372# format for drivers {"drivers":[{"color":"#fff500","label":"RIC","value":"RIC"},{"color":"#ff8700","label":"NOR","value":"NOR"},{"color":"#c00000","label":"VET","value":"VET"},{"color":"#0082fa","label":"LAT","value":"LAT"},{"color":"#787878","label":"GRO","value":"GRO"},{"color":"#ffffff","label":"GAS","value":"GAS"},{"color":"#f596c8","label":"STR","value":"STR"},{"color":"#787878","label":"MAG","value":"MAG"},{"color":"#0600ef","label":"ALB","value":"ALB"},{"color":"#ffffff","label":"KVY","value":"KVY"},{"color":"#fff500","label":"OCO","value":"OCO"},{"color":"#0600ef","label":"VER","value":"VER"},{"color":"#00d2be","label":"HAM","value":"HAM"},{"color":"#ff8700","label":"SAI","value":"SAI"},{"color":"#00d2be","label":"BOT","value":"BOT"},{"color":"#960000","label":"GIO","value":"GIO"}]}373 374# @st.cache_data375 376 377@functools.cache378@app.get("/strategy/{year}/{event}", response_model=None)379async def get_strategy(year: int, event: str | int) -> any:380 f1session = fastf1.get_session(year, event, "R")381 f1session.load(telemetry=False, weather=False, messages=False)382 laps = f1session.laps383 384 drivers_list = pd.unique(laps["Driver"])385 386 drivers = pd.DataFrame(drivers_list, columns=["Driver"])387 drivers["FinishOrder"] = drivers.index + 1388 389 # Get the LapNumber of the first lap of each stint390 first_lap = (391 laps[["Driver", "Stint", "Compound", "LapNumber"]]392 .groupby(["Driver", "Stint", "Compound"])393 .first()394 .reset_index()395 )396 # Add FinishOrder to first_lap397 first_lap = pd.merge(first_lap, drivers, on="Driver")398 # change LapNumber to LapStart399 first_lap = first_lap.rename(columns={"LapNumber": "LapStart"})400 # reduce the lapstart by 1401 first_lap["LapStart"] = first_lap["LapStart"] - 1402 403 # find the last lap of each stint404 last_lap = (405 laps[["Driver", "Stint", "Compound", "LapNumber"]]406 .groupby(["Driver", "Stint", "Compound"])407 .last()408 .reset_index()409 )410 # change LapNumber to LapEnd411 last_lap = last_lap.rename(columns={"LapNumber": "LapEnd"})412 413 # combine first_lap and last_lap414 stint_laps = pd.merge(first_lap, last_lap, on=["Driver", "Stint", "Compound"])415 # to cover for outliers416 stint_laps["fill"] = "white"417 418 stint_laps["fill"] = stint_laps["Compound"].map(419 {420 "SOFT": "red",421 "MEDIUM": "yellow",422 "HARD": "white",423 "INTERMEDIATE": "blue",424 "WET": "green",425 }426 )427 428 # sort by FinishOrder429 stint_laps = stint_laps.sort_values(by=["FinishOrder"], ascending=[True])430 431 stint_laps_dict = stint_laps.to_dict("records")432 433 return {"strategy": stint_laps_dict}434 435 436@functools.cache437@app.get("/lapchart/{year}/{event}/{session}", response_model=None)438async def lap_chart(439 year: int,440 event: str | int,441 session: str,442) -> any:443 ergast = Ergast()444 445 race_names_df = ergast.get_race_schedule(season=year, result_type="pandas")446 event_number = race_names_df[race_names_df["raceName"] == event]["round"].values[0]447 drivers_df = ergast.get_driver_info(448 season=year, round=event_number, result_type="pandas"449 )450 laptimes_df = ergast.get_lap_times(451 season=year, round=event_number, result_type="pandas", limit=2000452 ).content[0]453 laptimes_df = pd.merge(laptimes_df, drivers_df, how="left", on="driverId")454 455 results_df = ergast.get_race_results(456 season=year, round=event_number, result_type="pandas"457 ).content[0]458 results_df = results_df[["driverCode", "constructorName"]]459 460 # merge results_df on laptime_df461 laptimes_df = pd.merge(laptimes_df, results_df, how="left", on="driverCode")462 463 team_colors = utils.team_colors(year)464 # add team_colors to laptimes_df465 laptimes_df["fill"] = laptimes_df["constructorName"].map(team_colors)466 467 # rename number as x and position as y468 laptimes_df.rename(469 columns={"number": "x", "position": "y", "driverCode": "id"}, inplace=True470 )471 472 lap_chart_data = []473 474 for driver in laptimes_df["id"].unique():475 data = laptimes_df[laptimes_df["id"] == driver]476 fill = data["fill"].values[0]477 data = data[["x", "y"]]478 data_dict = data.to_dict(orient="records")479 driver_dict = {"id": driver, "fill": fill, "data": data_dict}480 # add this to all_data481 lap_chart_data.append(driver_dict)482 483 lap_chart_dict = {"lapChartData": lap_chart_data}484 485 return lap_chart_dict486 487 488@functools.cache489@app.get("/{year}/{event}/{session}", response_model=None)490async def session_drivers(year: int, event: str | int, session: str) -> any:491 # get drivers available for a given year, event and session492 f1session = fastf1.get_session(year, event, session)493 f1session.load(telemetry=False, weather=False, messages=False)494 495 laps = f1session.laps496 team_colors = utils.team_colors(year)497 # add team_colors dict to laps on Team column498 laps["color"] = laps["Team"].map(team_colors)499 500 unique_drivers = laps["Driver"].unique()501 502 drivers = [503 {504 "color": laps[laps.Driver == driver].color.iloc[0],505 "label": driver,506 "value": driver,507 }508 for driver in unique_drivers509 ]510 511 return {"drivers": drivers}512 513 514@functools.cache515@app.get("/laps/{year}/{event}/{session}", response_model=None)516async def get_driver_laps_data(year: int, event: str | int, session: str) -> any:517 # get drivers available for a given year, event and session518 f1session = fastf1.get_session(year, event, session)519 f1session.load(telemetry=False, weather=False, messages=False)520 laps = f1session.laps521 team_colors = utils.team_colors(year)522 # add team_colors dict to laps on Team column523 laps["color"] = laps["Team"].map(team_colors)524 525 # combine Driver and LapNumber as a new column526 laps["label"] = (527 laps["Driver"]528 + "-"529 + laps["LapNumber"].astype(int).astype(str)530 + "-"531 + str(year)532 + "-"533 + event534 + "-"535 + session536 )537 laps["value"] = (538 laps["Driver"]539 + "-"540 + laps["LapNumber"].astype(int).astype(str)541 + "-"542 + str(year)543 + "-"544 + event545 + "-"546 + session547 )548 549 laps = laps[["value", "label", "color"]]550 551 driver_laps_dict = laps.to_dict("records")552 553 return {"laps": driver_laps_dict}554 555 556# format for chartData {"chartData":[{"lapnumber":1},{557# "VER":91.564,558# "VER_compound":"SOFT",559# "VER_compound_color":"#FF5733",560# "lapnumber":2561# },{"lapnumber":3},{"VER":90.494,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":4},{"lapnumber":5},{"VER":90.062,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":6},{"lapnumber":7},{"VER":89.815,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":8},{"VER":105.248,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":9},{"lapnumber":10},{"VER":89.79,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":11},{"VER":145.101,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":12},{"lapnumber":13},{"VER":89.662,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":14},{"lapnumber":15},{"VER":89.617,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":16},{"lapnumber":17},{"VER":140.717,"VER_compound":"SOFT","VER_compound_color":"#FF5733","lapnumber":18}]}562 563# @st.cache_data564 565 566@functools.cache567@app.get("/{year}/{event}/{session}/{driver}", response_model=None)568async def laps_data(year: int, event: str | int, session: str, driver: str) -> any:569 # get drivers available for a given year, event and session570 f1session = fastf1.get_session(year, event, session)571 f1session.load(telemetry=False, weather=False, messages=False)572 laps = f1session.laps573 team_colors = utils.team_colors(year)574 # add team_colors dict to laps on Team column575 576 drivers = laps.Driver.unique()577 # for each driver in drivers, get the Team column from laps and get the color from team_colors dict578 drivers = [579 {580 "color": team_colors[laps[laps.Driver == driver].Team.iloc[0]],581 "label": driver,582 "value": driver,583 }584 for driver in drivers585 ]586 587 driver_laps = laps.pick_driver(driver)588 driver_laps["LapTime"] = driver_laps["LapTime"].dt.total_seconds()589 # remove rows where LapTime is null590 driver_laps = driver_laps[driver_laps.LapTime.notnull()]591 compound_colors = {592 "SOFT": "#FF0000",593 "MEDIUM": "#FFFF00",594 "HARD": "#FFFFFF",595 "INTERMEDIATE": "#00FF00",596 "WET": "#088cd0",597 }598 599 driver_laps_data = []600 601 for _, row in driver_laps.iterrows():602 if row["LapTime"] > 0:603 lap = {604 f"{driver}": row["LapTime"],605 f"{driver}_compound": row["Compound"],606 f"{driver}_compound_color": compound_colors[row["Compound"]],607 "lapnumber": row["LapNumber"],608 }609 else:610 lap = {"lapnumber": row["LapNumber"]}611 612 driver_laps_data.append(lap)613 614 return {"chartData": driver_laps_data}615 616 617@functools.cache618@app.get("/laptimes/{year}/{event}/{session}/{driver}", response_model=None)619async def get_laps_data(year: int, event: str | int, session: str, driver: str) -> any:620 # get drivers available for a given year, event and session621 f1session = fastf1.get_session(year, event, session)622 f1session.load(telemetry=False, weather=False, messages=False)623 laps = f1session.laps624 team_colors = utils.team_colors(year)625 # add team_colors dict to laps on Team column626 627 drivers = laps.Driver.unique()628 # for each driver in drivers, get the Team column from laps and get the color from team_colors dict629 drivers = [630 {631 "color": team_colors[laps[laps.Driver == driver].Team.iloc[0]],632 "label": driver,633 "value": driver,634 }635 for driver in drivers636 ]637 638 driver_laps = laps.pick_driver(driver)639 driver_laps["LapTime"] = driver_laps["LapTime"].dt.total_seconds()640 driver_laps = driver_laps[["Driver", "LapTime", "LapNumber", "Compound"]]641 642 # remove rows where LapTime is null643 driver_laps = driver_laps[driver_laps.LapTime.notnull()]644 645 driver_laps_dict = driver_laps.to_dict("records")646 647 return {"chartData": driver_laps_dict}648 649 650# @st.cache_data651 652 653@functools.cache654@app.get("/{year}/{event}/{session}/{driver}/{lap_number}", response_model=None)655async def telemetry_data(656 year: int, event: str | int, session: str, driver: str, lap_number: int657) -> any:658 f1session = fastf1.get_session(year, event, session)659 f1session.load(telemetry=True, weather=False, messages=False)660 laps = f1session.laps661 662 driver_laps = laps.pick_driver(driver)663 driver_laps["LapTime"] = driver_laps["LapTime"].dt.total_seconds()664 665 # get the telemetry for lap_number666 selected_lap = driver_laps[driver_laps.LapNumber == lap_number]667 668 telemetry = selected_lap.get_telemetry()669 670 lon_acc, lat_acc = accelerations.compute_accelerations(telemetry)671 telemetry["lon_acc"] = lon_acc672 telemetry["lat_acc"] = lat_acc673 674 telemetry["Time"] = telemetry["Time"].dt.total_seconds()675 676 laptime = selected_lap.LapTime.values[0]677 data_key = f"{driver} - Lap {int(lap_number)} - {year} {session} [laptime]"678 679 telemetry["DRS"] = telemetry["DRS"].apply(lambda x: 1 if x in [10, 12, 14] else 0)680 681 brake_tel = []682 drs_tel = []683 gear_tel = []684 rpm_tel = []685 speed_tel = []686 throttle_tel = []687 time_tel = []688 track_map = []689 lon_acc_tel = []690 lat_acc_tel = []691 692 for _, row in telemetry.iterrows():693 brake = {694 "x": row["Distance"],695 "y": row["Brake"],696 }697 brake_tel.append(brake)698 699 drs = {700 "x": row["Distance"],701 "y": row["DRS"],702 }703 drs_tel.append(drs)704 705 gear = {706 "x": row["Distance"],707 "y": row["nGear"],708 }709 gear_tel.append(gear)710 711 rpm = {712 "x": row["Distance"],713 "y": row["RPM"],714 }715 rpm_tel.append(rpm)716 717 speed = {718 "x": row["Distance"],719 "y": row["Speed"],720 }721 speed_tel.append(speed)722 723 throttle = {724 "x": row["Distance"],725 "y": row["Throttle"],726 }727 throttle_tel.append(throttle)728 729 time = {730 "x": row["Distance"],731 "y": row["Time"],732 }733 time_tel.append(time)734 735 lon_acc = {736 "x": row["Distance"],737 "y": row["lon_acc"],738 }739 lon_acc_tel.append(lon_acc)740 741 lat_acc = {742 "x": row["Distance"],743 "y": row["lat_acc"],744 }745 lat_acc_tel.append(lat_acc)746 747 track = {748 "x": row["X"],749 "y": row["Y"],750 }751 track_map.append(track)752 753 telemetry_data = {754 "telemetryData": {755 "brake": brake_tel,756 "dataKey": data_key,757 "drs": drs_tel,758 "gear": gear_tel,759 "rpm": rpm_tel,760 "speed": speed_tel,761 "throttle": throttle_tel,762 "time": time_tel,763 "lon_acc": lon_acc_tel,764 "lat_acc": lat_acc_tel,765 "trackMap": track_map,766 }767 }768 769 return telemetry_data770 