formula1 / app.py
kbberendsen's picture
Adding the Dutch grand prix
c8db353
import os
from shiny import App, ui, render, reactive, module
import fastf1 as ff1
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib import cm
import numpy as np
import shinyswatch
from modules import drivers
# 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 = drivers.drivers_2023
drivers_2022 = drivers.drivers_2022
app_ui = ui.page_fluid(
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", "Netherlands"],
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
@reactive.Effect()
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
@reactive.Calc
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
@reactive.Calc
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')
@output
@render.text
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}"
@output
@render.text
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}"
@output
@render.text
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}"
@output
@render.text
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}"
@output
@render.plot
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
@output
@render.plot
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)