| import pandas as pd |
| import streamlit as st |
|
|
| st.set_page_config(page_title="Seuils individualisés - Zebris", layout="wide") |
|
|
| st.title("Générateur de seuils individualisés") |
| st.caption("Poids + volume horaire d'entraînement → seuils personnalisés de référence") |
|
|
| st.subheader("Entrées athlète") |
|
|
| c1, c2 = st.columns(2) |
| with c1: |
| nom = st.text_input("Nom de l'athlète", value="Lucas Martin") |
| poids_kg = st.number_input("Poids (kg)", min_value=30.0, max_value=150.0, value=72.0, step=0.1) |
|
|
| with c2: |
| sport = st.text_input("Sport", value="Course sur route") |
| volume_horaire = st.number_input("Volume horaire / semaine", min_value=0.5, max_value=30.0, value=5.0, step=0.5) |
|
|
| poids_n = poids_kg * 9.81 |
|
|
| |
| if volume_horaire <= 3: |
| charge = "faible" |
| elif volume_horaire <= 6: |
| charge = "modérée" |
| else: |
| charge = "élevée" |
|
|
| st.info(f"Catégorie de charge retenue : **{charge}**") |
|
|
| |
| if charge == "faible": |
| force_bw_low = 0.75 |
| force_bw_high = 1.00 |
| pression_low = 20 |
| pression_high = 26 |
| cadence_low = 160 |
| cadence_high = 172 |
| contact_low = 70 |
| contact_high = 74 |
| flight_low = 26 |
| flight_high = 30 |
| asym_low = 6 |
| asym_high = 10 |
| rotation_low = 6 |
| rotation_high = 10 |
|
|
| elif charge == "modérée": |
| force_bw_low = 0.70 |
| force_bw_high = 0.95 |
| pression_low = 20 |
| pression_high = 25 |
| cadence_low = 165 |
| cadence_high = 176 |
| contact_low = 69 |
| contact_high = 73 |
| flight_low = 27 |
| flight_high = 31 |
| asym_low = 5 |
| asym_high = 9 |
| rotation_low = 5 |
| rotation_high = 9 |
|
|
| else: |
| force_bw_low = 0.65 |
| force_bw_high = 0.90 |
| pression_low = 20 |
| pression_high = 24 |
| cadence_low = 168 |
| cadence_high = 180 |
| contact_low = 68 |
| contact_high = 72 |
| flight_low = 28 |
| flight_high = 32 |
| asym_low = 4 |
| asym_high = 8 |
| rotation_low = 4 |
| rotation_high = 8 |
|
|
| |
| force_n_low = force_bw_low * poids_n |
| force_n_high = force_bw_high * poids_n |
|
|
| st.subheader("Résumé athlète") |
| r1, r2, r3 = st.columns(3) |
| with r1: |
| st.metric("Poids", f"{poids_kg:.1f} kg") |
| with r2: |
| st.metric("Poids en Newton", f"{poids_n:.1f} N") |
| with r3: |
| st.metric("Charge", charge) |
|
|
| st.subheader("Seuils individualisés") |
|
|
| impact_df = pd.DataFrame({ |
| "Variable": [ |
| "Force talon normalisée", |
| "Force talon convertie", |
| "Pression talon", |
| ], |
| "Zone basse / faible": [ |
| f"< {force_bw_low:.2f} BW", |
| f"< {force_n_low:.1f} N", |
| f"< {pression_low} N/cm²", |
| ], |
| "Zone attendue": [ |
| f"{force_bw_low:.2f} à {force_bw_high:.2f} BW", |
| f"{force_n_low:.1f} à {force_n_high:.1f} N", |
| f"{pression_low} à {pression_high} N/cm²", |
| ], |
| "Zone haute / élevée": [ |
| f"> {force_bw_high:.2f} BW", |
| f"> {force_n_high:.1f} N", |
| f"> {pression_high} N/cm²", |
| ], |
| }) |
|
|
| dynamique_df = pd.DataFrame({ |
| "Variable": [ |
| "Cadence", |
| "Temps de contact", |
| "Temps de vol", |
| ], |
| "Zone basse / faible": [ |
| f"< {cadence_low} pas/min", |
| f"< {contact_low} %", |
| f"< {flight_low} %", |
| ], |
| "Zone attendue": [ |
| f"{cadence_low} à {cadence_high} pas/min", |
| f"{contact_low} à {contact_high} %", |
| f"{flight_low} à {flight_high} %", |
| ], |
| "Zone haute / élevée": [ |
| f"> {cadence_high} pas/min", |
| f"> {contact_high} %", |
| f"> {flight_high} %", |
| ], |
| }) |
|
|
| symetrie_df = pd.DataFrame({ |
| "Variable": [ |
| "Asymétrie force talon", |
| "Asymétrie force avant-pied", |
| "Asymétrie COP", |
| "Différence rotation G/D", |
| ], |
| "Zone faible": [ |
| f"< {asym_low} %", |
| f"< {asym_low} %", |
| f"< {asym_low} %", |
| f"< {rotation_low}°", |
| ], |
| "Zone modérée": [ |
| f"{asym_low} à {asym_high} %", |
| f"{asym_low} à {asym_high} %", |
| f"{asym_low} à {asym_high} %", |
| f"{rotation_low} à {rotation_high}°", |
| ], |
| "Zone marquée": [ |
| f"> {asym_high} %", |
| f"> {asym_high} %", |
| f"> {asym_high} %", |
| f"> {rotation_high}°", |
| ], |
| }) |
|
|
| tab1, tab2, tab3 = st.tabs(["Impact", "Dynamique", "Symétrie"]) |
|
|
| with tab1: |
| st.dataframe(impact_df, hide_index=True, use_container_width=True) |
|
|
| with tab2: |
| st.dataframe(dynamique_df, hide_index=True, use_container_width=True) |
|
|
| with tab3: |
| st.dataframe(symetrie_df, hide_index=True, use_container_width=True) |
|
|
| st.subheader("Lecture méthodologique") |
| st.write( |
| "Ces seuils sont individualisés à partir du poids et du volume horaire d’entraînement hebdomadaire. " |
| "Ils constituent une base de référence personnelle de l’athlète avant toute analyse ou recommandation." |
| ) |