MaxBDKT commited on
Commit
12186ae
·
verified ·
1 Parent(s): 791780f

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +59 -69
src/streamlit_app.py CHANGED
@@ -4,47 +4,28 @@ import numpy as np
4
  import plotly.graph_objects as go
5
  import os
6
 
7
- # Configuration
8
  st.set_page_config(page_title="Brake Performance Lab", layout="wide")
9
 
10
- # --- CSS DE LA DERNIÈRE CHANCE (FORÇAGE NOIR SUR BLANC) ---
11
  st.markdown("""
12
  <style>
13
- /* 1. Fond blanc pur partout */
14
  .stApp, [data-testid="stSidebar"] { background-color: #FFFFFF !important; }
15
-
16
- /* 2. Compactage Sidebar */
17
  [data-testid="stSidebar"] [data-testid="stVerticalBlock"] { gap: 0.1rem !important; padding-top: 0rem !important; }
18
-
19
- /* 3. Force le NOIR sur TOUS les textes (Labels, Titres, Menus) */
20
- * { color: #000000 !important; font-family: sans-serif; }
21
 
22
- /* 4. Fix spécifique pour les LISTES DÉROULANTES (Selectbox / Multiselect) */
23
- /* On force le fond en blanc et la bordure en noir */
24
- div[data-baseweb="select"] {
25
- border: 2px solid #000000 !important;
26
- background-color: #FFFFFF !important;
27
- }
28
- /* On force le texte des options dans la liste qui s'ouvre */
29
- ul[role="listbox"] { background-color: #FFFFFF !important; border: 2px solid #000000 !important; }
30
- li[role="option"] { background-color: #FFFFFF !important; color: #000000 !important; }
31
- li[role="option"]:hover { background-color: #0082C3 !important; color: #FFFFFF !important; }
32
-
33
- /* Fix pour les tags (modèles sélectionnés) : Fond bleu, texte blanc pour qu'ils ressortent */
34
- [data-testid="stMultiSelect"] span {
35
- background-color: #0082C3 !important;
36
- color: #FFFFFF !important;
37
- }
38
 
39
- /* 5. Metrics et boîtes d'analyse en bas */
40
- [data-testid="column"] {
41
- padding: 8px !important; border: 2px solid #000000 !important;
42
- border-radius: 8px !important; background-color: #FFFFFF !important;
43
- }
44
- [data-testid="stMetricValue"] { font-weight: 800 !important; font-size: 20px !important; }
45
 
46
- /* On cache les indicateurs de flèches qui peuvent être gris */
47
- [data-testid="stMetricDelta"] svg { display: none; }
 
48
  </style>
49
  """, unsafe_allow_html=True)
50
 
@@ -52,9 +33,9 @@ st.markdown("""
52
  def load_data():
53
  current_dir = os.path.dirname(__file__)
54
  file_path = os.path.join(current_dir, "Brake_Lab_Test_Data.xlsx")
55
- data = pd.read_excel(file_path, sheet_name='Data')
56
- data.columns = data.columns.str.strip()
57
- return data
58
 
59
  try:
60
  df = load_data()
@@ -62,71 +43,80 @@ try:
62
 
63
  with st.sidebar:
64
  st.image("https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Decathlon_Logo.svg/1280px-Decathlon_Logo.svg.png", width=150)
65
- st.markdown("**SETTINGS**")
66
  x_input = st.slider("Lever Effort [N]", 40, 200, 100)
67
  selected_models = st.multiselect("Models", options=all_models, default=all_models[:2])
68
  norm_type = st.selectbox("Norm Category", ["None", "City/Trekking", "Kids", "MTB", "Racing"])
69
 
 
70
  n_dry, n_wet = 0, 0
71
  if norm_type == "City/Trekking": n_dry, n_wet = 340, 220
72
  elif norm_type == "Kids": n_dry, n_wet = 204, 132
73
  elif norm_type == "MTB": n_dry, n_wet = 425, 280
74
  elif norm_type == "Racing": n_dry, n_wet = 425, 260
75
 
76
- with st.expander("Options"):
77
- show_loss = st.checkbox("Show Loss", value=True)
78
- enable_comparison = st.checkbox("Enable Ref", value=True)
79
- ref_model = st.selectbox("Ref Model", options=all_models)
80
- condition_view = st.radio("View", ["Both", "Dry only", "Wet only"])
81
 
82
- # --- DIAGNOSTIC ---
83
- label, color = ("LIGHT", "#a1c4fd") if x_input < 70 else (("MODERATE", "#ffdb58") if x_input <= 110 else ("POWERFUL", "#ff4b4b"))
84
- st.markdown(f"<div style='background-color:{color}; padding:5px; border:2px solid #000; text-align:center; font-weight:bold;'>{label} BRAKING | {x_input} N</div>", unsafe_allow_html=True)
85
-
86
- # --- GRAPHIC (FIX NOIR TOTAL) ---
87
  filtered_df = df[df['model name'].isin(selected_models)]
 
 
 
 
 
88
  fig = go.Figure()
89
  x_range = np.linspace(40, 200, 100)
90
  colors = ['#0082C3', '#E63312', '#333333', '#00A14B', '#FFD200']
91
 
92
- row_ref = df[df['model name'] == ref_model].iloc[0]
93
- ref_d, ref_w = row_ref['dry a']*x_input+row_ref['dry b'], row_ref['wet a']*x_input+row_ref['wet b']
94
-
95
  for i, (idx, row) in enumerate(filtered_df.iterrows()):
96
- c = colors[i % len(colors)]
 
97
  if condition_view in ["Both", "Dry only"]:
98
- fig.add_trace(go.Scatter(x=x_range, y=row['dry a']*x_range+row['dry b'], name=f"{row['model name']} (D)", line=dict(color=c, width=4)))
 
 
99
  if condition_view in ["Both", "Wet only"]:
100
- fig.add_trace(go.Scatter(x=x_range, y=row['wet a']*x_range+row['wet b'], name=f"{row['model name']} (W)", line=dict(color=c, width=2, dash='dot')))
 
 
 
 
 
 
 
 
 
101
 
102
- # Fix pour les noms des AXES (on force le NOIR pur ici)
103
  fig.update_layout(
104
  height=450, plot_bgcolor='white', paper_bgcolor='white',
105
- xaxis=dict(title=dict(text="Lever Effort [N]", font=dict(color="black", size=14, family="Arial Black")),
106
- tickfont=dict(color="black", size=12, weight=700), linecolor="black", linewidth=2, gridcolor="#EEE"),
107
- yaxis=dict(title=dict(text="Performance [N]", font=dict(color="black", size=14, family="Arial Black")),
108
- tickfont=dict(color="black", size=12, weight=700), linecolor="black", linewidth=2, gridcolor="#EEE"),
109
- legend=dict(font=dict(color="black", weight=700), bordercolor="black", borderwidth=1),
110
- hovermode="x unified"
111
  )
112
  st.plotly_chart(fig, use_container_width=True)
113
 
114
- # --- ANALYSIS ---
115
- st.markdown("**ANALYSIS**")
116
  if not filtered_df.empty:
117
  cols = st.columns(len(filtered_df))
118
  for i, (idx, row) in enumerate(filtered_df.iterrows()):
119
  with cols[i]:
120
  st.markdown(f"**{row['model name']}**")
121
- d_val = round(row['dry a']*x_input + row['dry b'], 1)
122
- w_val = round(row['wet a']*x_input + row['wet b'], 1)
123
 
124
  if condition_view in ["Both", "Dry only"]:
125
- st.metric("Dry", f"{d_val}N", f"{d_val-round(ref_d,1):+.1f}N Vs Ref" if enable_comparison and row['model name']!=ref_model else None)
126
  if condition_view in ["Both", "Wet only"]:
127
- st.metric("Wet", f"{w_val}N", f"{w_val-round(ref_w,1):+.1f}N Vs Ref" if enable_comparison and row['model name']!=ref_model else None)
128
- if show_loss and condition_view=="Both":
129
- loss = ((d_val - w_val) / d_val * 100) if d_val != 0 else 0
130
- st.metric("Loss", f"-{round(loss,1)}%", f"{round(w_val-d_val,1)}N vs Dry", delta_color="inverse")
 
 
 
 
131
  except Exception as e:
132
- st.error(f"Error: {e}")
 
4
  import plotly.graph_objects as go
5
  import os
6
 
7
+ # Configuration de la page
8
  st.set_page_config(page_title="Brake Performance Lab", layout="wide")
9
 
10
+ # --- CSS FINAL : FORÇAGE NOIR SUR BLANC + COMPACTAGE ---
11
  st.markdown("""
12
  <style>
 
13
  .stApp, [data-testid="stSidebar"] { background-color: #FFFFFF !important; }
 
 
14
  [data-testid="stSidebar"] [data-testid="stVerticalBlock"] { gap: 0.1rem !important; padding-top: 0rem !important; }
15
+ * { color: #000000 !important; }
 
 
16
 
17
+ /* Dropdowns et listes */
18
+ div[data-baseweb="select"] { border: 2px solid #000 !important; background-color: #FFF !important; }
19
+ ul[role="listbox"] { background-color: #FFFFFF !important; border: 2px solid #000 !important; }
20
+ li[role="option"] { background-color: #FFFFFF !important; color: #000 !important; }
21
+ li[role="option"]:hover { background-color: #0082C3 !important; color: #FFF !important; }
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ /* Tags Multiselect */
24
+ [data-testid="stMultiSelect"] span { background-color: #0082C3 !important; color: #FFFFFF !important; }
 
 
 
 
25
 
26
+ /* Metrics */
27
+ [data-testid="column"] { border: 2px solid #000 !important; border-radius: 8px !important; padding: 8px !important; }
28
+ [data-testid="stMetricValue"] { font-weight: 800 !important; }
29
  </style>
30
  """, unsafe_allow_html=True)
31
 
 
33
  def load_data():
34
  current_dir = os.path.dirname(__file__)
35
  file_path = os.path.join(current_dir, "Brake_Lab_Test_Data.xlsx")
36
+ df = pd.read_excel(file_path, sheet_name='Data')
37
+ df.columns = df.columns.str.strip()
38
+ return df
39
 
40
  try:
41
  df = load_data()
 
43
 
44
  with st.sidebar:
45
  st.image("https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Decathlon_Logo.svg/1280px-Decathlon_Logo.svg.png", width=150)
46
+ st.markdown("### **SETTINGS**")
47
  x_input = st.slider("Lever Effort [N]", 40, 200, 100)
48
  selected_models = st.multiselect("Models", options=all_models, default=all_models[:2])
49
  norm_type = st.selectbox("Norm Category", ["None", "City/Trekking", "Kids", "MTB", "Racing"])
50
 
51
+ # Définition des seuils de norme
52
  n_dry, n_wet = 0, 0
53
  if norm_type == "City/Trekking": n_dry, n_wet = 340, 220
54
  elif norm_type == "Kids": n_dry, n_wet = 204, 132
55
  elif norm_type == "MTB": n_dry, n_wet = 425, 280
56
  elif norm_type == "Racing": n_dry, n_wet = 425, 260
57
 
58
+ with st.expander("Display Options"):
59
+ ref_model = st.selectbox("Benchmark Ref", options=all_models, index=0)
60
+ condition_view = st.radio("View", ["Both", "Dry only", "Wet only"], index=0)
 
 
61
 
62
+ # --- CALCULS ---
 
 
 
 
63
  filtered_df = df[df['model name'].isin(selected_models)]
64
+ row_ref = df[df['model name'] == ref_model].iloc[0]
65
+ ref_d = row_ref['dry a'] * x_input + row_ref['dry b']
66
+ ref_w = row_ref['wet a'] * x_input + row_ref['wet b']
67
+
68
+ # --- GRAPH ---
69
  fig = go.Figure()
70
  x_range = np.linspace(40, 200, 100)
71
  colors = ['#0082C3', '#E63312', '#333333', '#00A14B', '#FFD200']
72
 
 
 
 
73
  for i, (idx, row) in enumerate(filtered_df.iterrows()):
74
+ color = colors[i % len(colors)]
75
+ # Courbe SEC
76
  if condition_view in ["Both", "Dry only"]:
77
+ fig.add_trace(go.Scatter(x=x_range, y=row['dry a']*x_range + row['dry b'],
78
+ name=f"{row['model name']} (Dry)", line=dict(color=color, width=4)))
79
+ # Courbe HUMIDE
80
  if condition_view in ["Both", "Wet only"]:
81
+ fig.add_trace(go.Scatter(x=x_range, y=row['wet a']*x_range + row['wet b'],
82
+ name=f"{row['model name']} (Wet)", line=dict(color=color, width=2, dash='dot')))
83
+
84
+ # Ajout des lignes de NORMES (Lignes horizontales noires)
85
+ if n_dry > 0 and condition_view in ["Both", "Dry only"]:
86
+ fig.add_hline(y=n_dry, line_width=3, line_color="black", annotation_text=f"NORM DRY {n_dry}N", annotation_font=dict(color="black", size=12))
87
+ if n_wet > 0 and condition_view in ["Both", "Wet only"]:
88
+ fig.add_hline(y=n_wet, line_width=3, line_dash="dot", line_color="black", annotation_text=f"NORM WET {n_wet}N", annotation_font=dict(color="black", size=12))
89
+
90
+ fig.add_vline(x=x_input, line_width=2, line_dash="dash", line_color="black")
91
 
 
92
  fig.update_layout(
93
  height=450, plot_bgcolor='white', paper_bgcolor='white',
94
+ xaxis=dict(title="Lever Effort [N]", gridcolor="#EEE", tickfont=dict(color="black", weight=700), titlefont=dict(color="black", size=14)),
95
+ yaxis=dict(title="Performance [N]", gridcolor="#EEE", tickfont=dict(color="black", weight=700), titlefont=dict(color="black", size=14)),
96
+ legend=dict(font=dict(color="black", weight=700), bordercolor="black", borderwidth=1)
 
 
 
97
  )
98
  st.plotly_chart(fig, use_container_width=True)
99
 
100
+ # --- DASHBOARD BAS ---
101
+ st.markdown("### **PERFORMANCE ANALYSIS**")
102
  if not filtered_df.empty:
103
  cols = st.columns(len(filtered_df))
104
  for i, (idx, row) in enumerate(filtered_df.iterrows()):
105
  with cols[i]:
106
  st.markdown(f"**{row['model name']}**")
107
+ d_val = row['dry a'] * x_input + row['dry b']
108
+ w_val = row['wet a'] * x_input + row['wet b']
109
 
110
  if condition_view in ["Both", "Dry only"]:
111
+ st.metric("Dry", f"{round(d_val,1)} N", f"{d_val-ref_d:+.1f} N vs Ref" if row['model name']!=ref_model else None)
112
  if condition_view in ["Both", "Wet only"]:
113
+ st.metric("Wet", f"{round(w_val,1)} N", f"{w_val-ref_w:+.1f} N vs Ref" if row['model name']!=ref_model else None)
114
+
115
+ # Check Conformité Norme
116
+ if n_dry > 0:
117
+ needed_dry = (n_dry - row['dry b']) / row['dry a']
118
+ if needed_dry > 180: st.error("❌ DRY NON-COMPLIANT")
119
+ else: st.success("✅ DRY OK")
120
+
121
  except Exception as e:
122
+ st.error(f"Erreur d'affichage : {e}")