cloud-sean's picture
Create app.py
225cf8d verified
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.
""")