Spaces:
Runtime error
Runtime error
import os | |
from shiny import App, ui, render, reactive | |
import fastf1 as ff1 | |
import matplotlib.pyplot as plt | |
from matplotlib.collections import LineCollection | |
from matplotlib import cm | |
import numpy as np | |
import shinyswatch | |
# Define cache folder path | |
cache_path = os.getcwd() + "/cache" | |
print(f"Cache path: {cache_path}") | |
ff1.Cache.enable_cache(cache_path) | |
# Offline mode to prevent F1 API crashes on Hugging Face | |
ff1.Cache.offline_mode(enabled=True) | |
# Define drivers | |
drivers_2023 = {'Fastest driver': 'Fastest driver', 'VER': 'Max Verstappen', | |
'NOR': 'Lando Norris', 'GAS': 'Pierre Gasly', 'PER': 'Sergio Perez', | |
'ALO': 'Fernando Alonso', 'LEC': 'Charles Leclerc', 'STR': 'Lance Stroll', | |
'MAG': 'Kevin Magnussen', 'TSU': 'Yuki Tsunoda', 'ALB': 'Alexander Albon', | |
'ZHO': 'Guanyu Zhou', 'HUL': 'Nico Hülkenberg', 'OCO': 'Esteban Ocon', | |
'HAM': 'Lewis Hamilton', 'SAI': 'Carlos Sainz', 'RUS': 'George Russel', | |
'BOT': 'Valteri Bottas', 'PIA': 'Oscar Piastri', 'VRI': 'Nyck de Vries', | |
'SAR': 'Logan Sargeant', 'RIC': 'Daniel Ricciardo'} | |
drivers_2022 = {'Fastest driver': 'Fastest driver', 'VER': 'Max Verstappen', | |
'NOR': 'Lando Norris', 'GAS': 'Pierre Gasly', 'PER': 'Sergio Perez', | |
'ALO': 'Fernando Alonso', 'LEC': 'Charles Leclerc', 'STR': 'Lance Stroll', | |
'MAG': 'Kevin Magnussen', 'TSU': 'Yuki Tsunoda', 'ALB': 'Alexander Albon', | |
'ZHO': 'Guanyu Zhou', 'HUL': 'Nico Hülkenberg', 'OCO': 'Esteban Ocon', | |
'HAM': 'Lewis Hamilton', 'SAI': 'Carlos Sainz', 'RUS': 'George Russel', | |
'BOT': 'Valteri Bottas', 'VRI': 'Nyck de Vries', 'VET': 'Sebastian Vettel', | |
'RIC': 'Daniel Ricciardo', 'MSC': 'Mick Schumacher', | |
'LAT': 'Nicolas Latifi'} | |
app_ui = ui.page_fluid( | |
{"style": "padding: 10px"}, | |
shinyswatch.theme.minty(), | |
ui.include_css("styles.css"), | |
ui.panel_title("Gear usage in fastest lap"), | |
ui.layout_sidebar( | |
ui.panel_sidebar( | |
ui.input_select( | |
"track_select", "Select track:", | |
choices = ["Austria", "Hungary", "Spain", "Bahrain", "United-Kingdom"], | |
selected = "Austria" | |
), | |
ui.input_radio_buttons( | |
"session_type", "Session type:", | |
choices = {"R": "Race", "Q": "Qualification"}, | |
selected = "R" | |
), | |
ui.input_radio_buttons( | |
"year", "Year:", | |
choices = ["2023", "2022"], | |
selected = "2023" | |
), | |
width=2 | |
), | |
ui.panel_main( | |
ui.row( | |
ui.column( | |
6, | |
ui.input_select( | |
"driver1_select", label="Select driver 1:", | |
choices = ["Fastest driver"], | |
selected = "Fastest driver" | |
) | |
), | |
ui.column( | |
6, | |
ui.input_select( | |
"driver2_select", label="Select driver 2:", | |
choices = ["Fastest driver"], | |
selected = "Fastest driver" | |
) | |
) | |
), | |
ui.row( | |
ui.column( | |
6, | |
ui.output_plot("gear_1"), | |
ui.output_text("fastest_driver_1"), | |
ui.output_text("laptime_1") | |
), | |
ui.column( | |
6, | |
ui.output_plot("gear_2"), | |
ui.output_text("fastest_driver_2"), | |
ui.output_text("laptime_2") | |
) | |
), | |
), | |
), | |
) | |
def server(input, output, session): | |
# Updating driver selection list | |
def _(): | |
if input.year() == "2023": | |
driver_options = drivers_2023 | |
elif input.year() == "2022": | |
driver_options = drivers_2022 | |
ui.update_select("driver1_select", | |
label="Select driver 1:", | |
choices=driver_options, | |
selected=input.driver1_select() | |
) | |
ui.update_select("driver2_select", | |
label="Select driver 2:", | |
choices=driver_options, | |
selected=input.driver2_select() | |
) | |
# Get required data for driver 2 based on selection | |
def get_data_1(): | |
try: | |
ui.notification_show("Data takes a couple seconds to load.", duration=3, type = 'default') | |
f1_session = ff1.get_session(int(input.year()), input.track_select(), input.session_type()) | |
f1_session.load() | |
# Check if user input == fastest driver | |
if input.driver1_select() == "Fastest driver": | |
lap = f1_session.laps.pick_fastest() | |
else: | |
laps_driver = f1_session.laps.pick_driver(input.driver1_select()) | |
lap = laps_driver.pick_fastest() | |
tel = lap.get_telemetry() | |
driver = lap['Driver'] | |
#converting data to numpy data tables | |
x = np.array(tel['X'].values) | |
y = np.array(tel['Y'].values) | |
points = np.array([x, y]).T.reshape(-1, 1, 2) | |
segments = np.concatenate([points[:-1], points[1:]], axis=1) | |
gear = tel['nGear'].to_numpy().astype(float) | |
lap_time = lap['LapTime'] | |
return segments, gear, driver, lap_time | |
except Exception: | |
ui.notification_show("Data not available. Select another track or driver.", duration=10, type = 'error') | |
# Get required data for driver 2 based on selection | |
def get_data_2(): | |
try: | |
ui.notification_show("Data takes a couple seconds to load.", duration=3, type = 'default') | |
f1_session = ff1.get_session(int(input.year()), input.track_select(), input.session_type()) | |
f1_session.load() | |
# Check if user input == fastest driver | |
if input.driver2_select() == "Fastest driver": | |
lap = f1_session.laps.pick_fastest() | |
else: | |
laps_driver = f1_session.laps.pick_driver(input.driver2_select()) | |
lap = laps_driver.pick_fastest() | |
tel = lap.get_telemetry() | |
driver = lap['Driver'] | |
#converting data to numpy data tables | |
x = np.array(tel['X'].values) | |
y = np.array(tel['Y'].values) | |
points = np.array([x, y]).T.reshape(-1, 1, 2) | |
segments = np.concatenate([points[:-1], points[1:]], axis=1) | |
gear = tel['nGear'].to_numpy().astype(float) | |
lap_time = lap['LapTime'] | |
return segments, gear, driver, lap_time | |
except Exception: | |
ui.notification_show("Data not available. Select another track or driver.", duration=10, type = 'error') | |
def fastest_driver_1(): | |
segments, gear, driver, lap_time = get_data_1() | |
#print(f"The driver of the fastest lap this session is: {driver}") | |
return f"Graph shows the fastest lap of: {driver}" | |
def fastest_driver_2(): | |
segments, gear, driver, lap_time = get_data_2() | |
#print(f"The driver of the fastest lap this session is: {driver}") | |
return f"Graph shows the fastest lap of: {driver}" | |
def laptime_1(): | |
segments, gear, driver, lap_time = get_data_1() | |
delta_str= str(lap_time) | |
# Split the time delta string to extract hours, minutes, and seconds | |
time_parts = delta_str.split(" ")[-1].split(":") | |
hours, minutes, seconds = map(float, time_parts) | |
# Convert the extracted values to the desired format | |
formatted_time = "{:02d}:{:06.3f}".format(int(hours * 60 + minutes), seconds) | |
return f"The lap time is: {formatted_time}" | |
def laptime_2(): | |
segments, gear, driver, lap_time = get_data_2() | |
delta_str= str(lap_time) | |
# Split the time delta string to extract hours, minutes, and seconds | |
time_parts = delta_str.split(" ")[-1].split(":") | |
hours, minutes, seconds = map(float, time_parts) | |
# Convert the extracted values to the desired format | |
formatted_time = "{:02d}:{:06.3f}".format(int(hours * 60 + minutes), seconds) | |
return f"The lap time is: {formatted_time}" | |
def gear_1(): | |
try: | |
segments, gear, driver, lap_time = get_data_1() | |
cmap = cm.get_cmap('Paired') | |
lc_comp = LineCollection(segments, norm=plt.Normalize(1, cmap.N+1), cmap=cmap) | |
lc_comp.set_array(gear) | |
lc_comp.set_linewidth(4) | |
plt.gca().add_collection(lc_comp) | |
plt.axis('equal') | |
plt.tick_params(labelleft=False, left=False, labelbottom=False, bottom=False) | |
cbar = plt.colorbar(mappable=lc_comp, label="Gear", boundaries=np.arange(1, 10)) | |
cbar.set_ticks(np.arange(1.5, 9.5)) | |
cbar.set_ticklabels(np.arange(1, 9)) | |
plt | |
except Exception: | |
pass | |
def gear_2(): | |
try: | |
segments, gear, driver, lap_time = get_data_2() | |
cmap = cm.get_cmap('Paired') | |
lc_comp = LineCollection(segments, norm=plt.Normalize(1, cmap.N+1), cmap=cmap) | |
lc_comp.set_array(gear) | |
lc_comp.set_linewidth(4) | |
plt.gca().add_collection(lc_comp) | |
plt.axis('equal') | |
plt.tick_params(labelleft=False, left=False, labelbottom=False, bottom=False) | |
cbar = plt.colorbar(mappable=lc_comp, label="Gear", boundaries=np.arange(1, 10)) | |
cbar.set_ticks(np.arange(1.5, 9.5)) | |
cbar.set_ticklabels(np.arange(1, 9)) | |
plt | |
except Exception: | |
pass | |
app = App(app_ui, server) |