import streamlit as st import pandas as pd import plotly.graph_objects as go import plotly.express as px st.set_page_config(page_title="Interactive 12 Pubs of Christmas", layout="wide") # ------------------------------------ # Parameters & Constants # ------------------------------------ # Julia's data julia_weight_lbs = 120 julia_weight_kg = julia_weight_lbs * 0.45359237 julia_weight_g = julia_weight_kg * 1000 julia_r = 0.55 # Sean's data sean_weight_kg = 86.0 sean_weight_g = sean_weight_kg * 1000 sean_r = 0.68 beta = 0.015 # g/dL/h elimination rate g_per_unit = 8 # Drink options # We'll define a few drink types with their volumes and ABVs. # For simplicity, we assume: # - 1 UK pint = 568 ml # - Half beer = Half pint = 284 ml # - Shot = 30 ml # - ABV is given in percentage. # We'll approximate each drink's ethanol mass: # Ethanol mass (g) = volume (ml) * density_of_ethanol(0.789 g/ml) * (ABV/100) # For simplicity, we use a units-based approach: # 1 unit (UK) = 10ml pure ethanol = 8g ethanol # Units = (Volume in ml * ABV%) / 1000 (approx UK definition: 1 unit = 10ml pure ethanol) # So ethanol in grams = Units * 8. drink_options = { "Guinness (Pint)": {"volume_ml": 568, "abv": 4.2}, "Heineken (Pint)": {"volume_ml": 568, "abv": 5.0}, "Stella Artois (Pint)": {"volume_ml": 568, "abv": 4.8}, "Half Beer (Generic)": {"volume_ml": 284, "abv": 4.0}, "Vodka Shot": {"volume_ml": 30, "abv": 40.0}, "Whiskey Shot": {"volume_ml": 30, "abv": 40.0}, "Cider (Pint)": {"volume_ml": 568, "abv": 4.5}, "Red Wine (175ml)": {"volume_ml": 175, "abv": 12.0} } # A function to calculate ethanol grams from a given drink def alcohol_in_grams(volume_ml, abv_percent): # 1 unit (UK) ~ (volume_ml * abv) / 1000 * (8g/unit) units = (volume_ml * abv_percent) / 1000 return units * 8 # grams of ethanol def bac_calculation(total_alcohol_g, W, r, t): # Widmark formula approximation: # BAC = (A/(W*r))*100 - beta*(t-1) if t>1 bac = (total_alcohol_g / (W * r)) * 100 if t > 1: bac = bac - beta*(t-1) return max(bac,0) st.title("12 Pubs of Christmas - Interactive Drink Selection") st.write(""" Select the drinks you (Julia) and Sean will consume each hour over a 6-hour period. We'll estimate your BAC levels and show you comparisons. **Note:** These are rough estimates and not medically accurate. Actual BAC varies by individual. """) hours = 6 col_drinks = st.columns(hours) selected_drinks = [] st.subheader("Select Drinks for Each Hour") for i in range(hours): with col_drinks[i]: drink_choice = st.selectbox( f"Hour {i+1} Drink", list(drink_options.keys()), key=f"drink_hour_{i}" ) selected_drinks.append(drink_choice) # Calculate total alcohol consumed per hour alcohol_by_hour = [] for i, d in enumerate(selected_drinks): vol = drink_options[d]["volume_ml"] abv = drink_options[d]["abv"] grams = alcohol_in_grams(vol, abv) alcohol_by_hour.append(grams) # Cumulative alcohol and BAC calculations time_points = list(range(hours+1)) # from 0 to 6 julia_bac = [] sean_bac = [] julia_alc_over_time = [] sean_alc_over_time = [] for t in time_points: # total alcohol consumed up to hour t total_alc = sum(alcohol_by_hour[:t]) # Julia BAC julia_bac_val = bac_calculation(total_alc, julia_weight_g, julia_r, t) julia_bac.append(julia_bac_val) # Sean BAC sean_bac_val = bac_calculation(total_alc, sean_weight_g, sean_r, t) sean_bac.append(sean_bac_val) df = pd.DataFrame({ "Hour": time_points, "Julia_BAC": julia_bac, "Sean_BAC": sean_bac, "Total_Alcohol_Consumed_g": [sum(alcohol_by_hour[:t]) for t in time_points] }) # Calculate difference df["BAC_Difference"] = df["Julia_BAC"] - df["Sean_BAC"] # Plot 1: BAC over time st.subheader("BAC over Time") fig_bac = go.Figure() fig_bac.add_trace(go.Scatter(x=df["Hour"], y=df["Julia_BAC"], mode='lines+markers', name="Julia BAC", line=dict(color='firebrick', width=3))) fig_bac.add_trace(go.Scatter(x=df["Hour"], y=df["Sean_BAC"], mode='lines+markers', name="Sean BAC", line=dict(color='royalblue', width=3))) fig_bac.update_layout(title="Estimated BAC vs. Time (Hours)", xaxis_title="Hours", yaxis_title="BAC (%)", template="plotly_white") st.plotly_chart(fig_bac, use_container_width=True) # Plot 2: Alcohol consumed vs BAC (Dual-Axis) st.subheader("Alcohol Consumed and BAC") fig_bar_line = go.Figure() # Bar for total alcohol consumed at each hour fig_bar_line.add_trace(go.Bar(x=df["Hour"], y=df["Total_Alcohol_Consumed_g"], name="Total Alcohol (g)", marker_color='goldenrod', yaxis='y1')) # Julia BAC line fig_bar_line.add_trace(go.Scatter(x=df["Hour"], y=df["Julia_BAC"], name="Julia BAC", mode='lines+markers', line=dict(color='firebrick', width=3), yaxis='y2')) # Sean BAC line fig_bar_line.add_trace(go.Scatter(x=df["Hour"], y=df["Sean_BAC"], name="Sean BAC", mode='lines+markers', line=dict(color='royalblue', width=3), yaxis='y2')) fig_bar_line.update_layout( title="Total Alcohol (g) vs BAC (%) Over Time", xaxis=dict(title="Hour"), yaxis=dict(title="Total Alcohol (g)", side='left', range=[0, df["Total_Alcohol_Consumed_g"].max()*1.1]), yaxis2=dict(title="BAC (%)", side='right', overlaying='y', range=[0, df[["Julia_BAC","Sean_BAC"]].max().max()*1.1]), template="plotly_white" ) st.plotly_chart(fig_bar_line, use_container_width=True) # Plot 3: Difference in BAC st.subheader("Difference in BAC (Julia - Sean)") fig_diff = go.Figure(go.Bar( x=df["Hour"], y=df["BAC_Difference"], text=[f"{d:.3f}%" for d in df["BAC_Difference"]], textposition='outside', marker_color='indianred' )) fig_diff.update_layout( title="Difference in BAC Between Julia and Sean Over Time", xaxis_title="Hour", yaxis_title="Difference in BAC (%)", template="plotly_white" ) st.plotly_chart(fig_diff, use_container_width=True) # Plot 4: Pie/Donut Chart of Alcohol Proportions if needed # For a bit of fun, let's compare the total alcohol consumed to their body weight as a fraction st.subheader("Alcohol as a Percentage of Body Weight") # 1g = 0.001 kg julia_pct_body_weight = (df["Total_Alcohol_Consumed_g"].iloc[-1] / julia_weight_g)*100 sean_pct_body_weight = (df["Total_Alcohol_Consumed_g"].iloc[-1] / sean_weight_g)*100 donut_data = pd.DataFrame({ "Person": ["Julia", "Sean"], "Alcohol_as_pct_body_weight": [julia_pct_body_weight, sean_pct_body_weight] }) fig_donut = px.pie(donut_data, values='Alcohol_as_pct_body_weight', names='Person', hole=0.5, color='Person', color_discrete_map={"Julia":"firebrick","Sean":"royalblue"}) fig_donut.update_layout(title="Alcohol Consumed as % of Body Weight", template="plotly_white") st.plotly_chart(fig_donut, use_container_width=True) st.write(""" **Disclaimer:** These BAC values are rough estimates. Actual impairment depends on many factors including metabolism, recent food intake, and individual tolerance. This tool is for illustrative purposes only. """)