MaxBDKT commited on
Commit
7d75a7b
Β·
verified Β·
1 Parent(s): dfdc829

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +63 -63
src/streamlit_app.py CHANGED
@@ -11,7 +11,6 @@ st.set_page_config(page_title="Brake Performance Lab", layout="wide", page_icon=
11
  def load_data():
12
  current_dir = os.path.dirname(__file__)
13
  file_path = os.path.join(current_dir, "Brake_Lab_Test_Data.xlsx")
14
- # Reading 'Data' sheet as specified
15
  data = pd.read_excel(file_path, sheet_name='Data')
16
  data.columns = data.columns.str.strip()
17
  return data
@@ -19,116 +18,117 @@ def load_data():
19
  try:
20
  df = load_data()
21
 
22
- # --- SIDEBAR (ADVANCED FILTERS) ---
23
  with st.sidebar:
24
- # Decathlon International Logo
25
  st.image("https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Decathlon_Logo.svg/1280px-Decathlon_Logo.svg.png", width=200)
26
  st.title("βš™οΈ Settings")
27
-
28
- # 1. Lever Effort Input
29
- x_input = st.slider("🫱 Lever Effort (N)", 40, 200, 100, help="Force applied to the brake lever in Newtons")
30
 
31
  st.markdown("---")
32
- st.subheader("πŸ” Graph Filters")
 
 
33
 
34
- # 2. Model Selection
35
  all_models = df['model name'].unique().tolist()
36
- selected_models = st.multiselect(
37
- "Select Models",
38
- options=all_models,
39
- default=all_models[:2] if len(all_models) > 1 else all_models
40
- )
41
-
42
- # 3. Condition Selection
43
- condition_view = st.radio(
44
- "Conditions to display",
45
- ["Both", "Dry only", "Wet only"],
46
- index=0
47
- )
48
 
49
- # --- DIAGNOSTIC LOGIC ---
50
  if x_input < 70:
51
- label, color_alert = "❄️ LIGHT BRAKING", "#a1c4fd" # Light Blue
52
  elif 70 <= x_input <= 110:
53
- label, color_alert = "βš–οΈ MODERATE BRAKING", "#ffdb58" # Yellow
54
  else:
55
- label, color_alert = "πŸ”₯ POWERFUL BRAKING", "#ff4b4b" # Red
56
 
57
- # Diagnostic Header
58
  st.markdown(f"""
59
  <div style="background-color:{color_alert}; padding:15px; border-radius:10px; text-align:center; border: 1px solid #ddd;">
60
  <h2 style="color:black; margin:0;">{label}</h2>
61
- <p style="color:black; font-weight:bold; margin:5px 0 0 0;">Current Lever Effort: {x_input} N</p>
62
  </div>
63
  """, unsafe_allow_html=True)
64
 
65
- # --- PLOT AREA ---
66
  col1, col2 = st.columns([3, 1])
67
 
68
  with col1:
69
  filtered_df = df[df['model name'].isin(selected_models)]
70
-
71
  fig = go.Figure()
72
  x_range = np.linspace(40, 200, 100)
73
- # Decathlon-friendly color palette
74
- colors = ['#0082C3', '#E63312', '#333333', '#00A14B', '#FFD200', '#AB63FA']
 
75
 
76
  for i, (index, row) in enumerate(filtered_df.iterrows()):
77
  color = colors[i % len(colors)]
 
 
78
 
79
- # Linear Regression Calculation: y = ax + b
80
- y_dry = row['dry a'] * x_range + row['dry b']
81
- y_wet = row['wet a'] * x_range + row['wet b']
 
 
 
 
 
82
 
83
- # DRY Trace
84
  if condition_view in ["Both", "Dry only"]:
85
- fig.add_trace(go.Scatter(x=x_range, y=y_dry, mode='lines',
86
  name=f"{row['model name']} (Dry)",
87
  line=dict(color=color, width=4),
88
- hovertemplate="Effort: %{x}N<br>Perf: %{y:.2f}"))
89
-
90
- # WET Trace
91
  if condition_view in ["Both", "Wet only"]:
92
- fig.add_trace(go.Scatter(x=x_range, y=y_wet, mode='lines',
93
  name=f"{row['model name']} (Wet)",
94
  line=dict(color=color, width=2, dash='dot'),
95
- hovertemplate="Effort: %{x}N<br>Perf: %{y:.2f}"))
96
 
97
- # Vertical line for current effort
98
  fig.add_vline(x=x_input, line_width=3, line_dash="dash", line_color="black")
99
 
100
  fig.update_layout(
101
  xaxis_title="Lever Effort (N)",
102
- yaxis_title="Braking Performance",
103
- legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
104
  plot_bgcolor='white',
105
- hovermode="x unified"
 
106
  )
 
 
 
 
107
  st.plotly_chart(fig, use_container_width=True)
108
 
109
  with col2:
110
  st.subheader("πŸ’‘ Analysis")
111
  if not filtered_df.empty:
112
- for index, row in filtered_df.iterrows():
113
- val_dry = row['dry a'] * x_input + row['dry b']
114
- val_wet = row['wet a'] * x_input + row['wet b']
115
- # Drop in efficiency calculation
116
- loss = ((val_dry - val_wet) / val_dry) * 100 if val_dry != 0 else 0
117
-
118
- st.metric(label=row['model name'],
119
- value=f"{round(val_dry, 2)}",
120
- delta=f"-{round(loss, 1)}% Wet Loss",
121
- delta_color="inverse")
122
- st.write(f"**Wet Perf:** {round(val_wet, 2)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  st.markdown("---")
124
  else:
125
- st.info("Please select at least one model in the sidebar.")
126
-
127
- # --- DATA EXPORT ---
128
- with st.expander("πŸ“‚ View Raw Data & Export"):
129
- st.dataframe(filtered_df, use_container_width=True)
130
- csv = filtered_df.to_csv(index=False).encode('utf-8')
131
- st.download_button("πŸ“₯ Download as CSV", csv, "brake_test_report.csv", "text/csv")
132
 
133
  except Exception as e:
134
- st.error(f"Technical Error: {e}")
 
11
  def load_data():
12
  current_dir = os.path.dirname(__file__)
13
  file_path = os.path.join(current_dir, "Brake_Lab_Test_Data.xlsx")
 
14
  data = pd.read_excel(file_path, sheet_name='Data')
15
  data.columns = data.columns.str.strip()
16
  return data
 
18
  try:
19
  df = load_data()
20
 
21
+ # --- SIDEBAR ---
22
  with st.sidebar:
 
23
  st.image("https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Decathlon_Logo.svg/1280px-Decathlon_Logo.svg.png", width=200)
24
  st.title("βš™οΈ Settings")
25
+ x_input = st.slider("🫱 Lever Effort (N)", 40, 200, 100)
 
 
26
 
27
  st.markdown("---")
28
+ st.subheader("πŸ” Display Options")
29
+ show_loss = st.checkbox("Show Efficiency Loss (Wet vs Dry)", value=True)
30
+ enable_comparison = st.checkbox("Enable Comparison Mode (Model vs Model)", value=False)
31
 
 
32
  all_models = df['model name'].unique().tolist()
33
+ selected_models = st.multiselect("Select Models", options=all_models, default=all_models[:2])
34
+ condition_view = st.radio("Conditions to display", ["Both", "Dry only", "Wet only"], index=0)
 
 
 
 
 
 
 
 
 
 
35
 
36
+ # --- DIAGNOSTIC HEADER ---
37
  if x_input < 70:
38
+ label, color_alert = "❄️ LIGHT BRAKING", "#a1c4fd"
39
  elif 70 <= x_input <= 110:
40
+ label, color_alert = "βš–οΈ MODERATE BRAKING", "#ffdb58"
41
  else:
42
+ label, color_alert = "πŸ”₯ POWERFUL BRAKING", "#ff4b4b"
43
 
 
44
  st.markdown(f"""
45
  <div style="background-color:{color_alert}; padding:15px; border-radius:10px; text-align:center; border: 1px solid #ddd;">
46
  <h2 style="color:black; margin:0;">{label}</h2>
47
+ <p style="color:black; font-weight:bold; margin:5px 0 0 0;">Lever Effort: {round(float(x_input), 1)} N</p>
48
  </div>
49
  """, unsafe_allow_html=True)
50
 
51
+ # --- MAIN AREA ---
52
  col1, col2 = st.columns([3, 1])
53
 
54
  with col1:
55
  filtered_df = df[df['model name'].isin(selected_models)]
 
56
  fig = go.Figure()
57
  x_range = np.linspace(40, 200, 100)
58
+ colors = ['#0082C3', '#E63312', '#333333', '#00A14B', '#FFD200']
59
+
60
+ comparison_results = []
61
 
62
  for i, (index, row) in enumerate(filtered_df.iterrows()):
63
  color = colors[i % len(colors)]
64
+ y_dry_val = row['dry a'] * x_input + row['dry b']
65
+ y_wet_val = row['wet a'] * x_input + row['wet b']
66
 
67
+ comparison_results.append({"name": row['model name'], "dry": y_dry_val, "wet": y_wet_val})
68
+
69
+ y_plot_dry = row['dry a'] * x_range + row['dry b']
70
+ y_plot_wet = row['wet a'] * x_range + row['wet b']
71
+
72
+ # Custom Hover Template: %{x} is removed from the body and put in the title
73
+ hover_dry = "<b>" + row['model name'] + " (Dry)</b><br>Perf: %{y:.1f}<extra></extra>"
74
+ hover_wet = "<b>" + row['model name'] + " (Wet)</b><br>Perf: %{y:.1f}<extra></extra>"
75
 
 
76
  if condition_view in ["Both", "Dry only"]:
77
+ fig.add_trace(go.Scatter(x=x_range, y=y_plot_dry, mode='lines',
78
  name=f"{row['model name']} (Dry)",
79
  line=dict(color=color, width=4),
80
+ hovertemplate=hover_dry))
 
 
81
  if condition_view in ["Both", "Wet only"]:
82
+ fig.add_trace(go.Scatter(x=x_range, y=y_plot_wet, mode='lines',
83
  name=f"{row['model name']} (Wet)",
84
  line=dict(color=color, width=2, dash='dot'),
85
+ hovertemplate=hover_wet))
86
 
 
87
  fig.add_vline(x=x_input, line_width=3, line_dash="dash", line_color="black")
88
 
89
  fig.update_layout(
90
  xaxis_title="Lever Effort (N)",
91
+ yaxis_title="Performance",
 
92
  plot_bgcolor='white',
93
+ hovermode="x unified", # Combine labels at the same X
94
+ hoverlabel=dict(bgcolor="white", font_size=12)
95
  )
96
+
97
+ # This part ensures the X value appears only once at the top of the unified hoverbox
98
+ fig.update_xaxes(showspikes=True, spikecolor="gray", spikesnap="cursor", spikemode="across")
99
+
100
  st.plotly_chart(fig, use_container_width=True)
101
 
102
  with col2:
103
  st.subheader("πŸ’‘ Analysis")
104
  if not filtered_df.empty:
105
+ max_dry = max([r['dry'] for r in comparison_results])
106
+ max_wet = max([r['wet'] for r in comparison_results])
107
+
108
+ for res in comparison_results:
109
+ st.write(f"### {res['name']}")
110
+ dry_val = round(res['dry'], 1)
111
+ if enable_comparison and len(selected_models) > 1:
112
+ diff_dry = round(res['dry'] - max_dry, 1)
113
+ suffix = " (Best)" if diff_dry == 0 else f" ({diff_dry} vs Best)"
114
+ st.write(f"**Dry:** {dry_val}{suffix}")
115
+ else:
116
+ st.write(f"**Dry:** {dry_val}")
117
+
118
+ wet_val = round(res['wet'], 1)
119
+ if enable_comparison and len(selected_models) > 1:
120
+ diff_wet = round(res['wet'] - max_wet, 1)
121
+ suffix = " (Best)" if diff_wet == 0 else f" ({diff_wet} vs Best)"
122
+ st.write(f"**Wet:** {wet_val}{suffix}")
123
+ else:
124
+ st.write(f"**Wet:** {wet_val}")
125
+
126
+ if show_loss:
127
+ loss = ((res['dry'] - res['wet']) / res['dry']) * 100 if res['dry'] != 0 else 0
128
+ st.warning(f"Efficiency Loss: -{round(loss, 1)}%")
129
  st.markdown("---")
130
  else:
131
+ st.info("Select models.")
 
 
 
 
 
 
132
 
133
  except Exception as e:
134
+ st.error(f"Error: {e}")