Spaces:
Sleeping
Sleeping
| import io | |
| import numpy as np | |
| import pandas as pd | |
| STANDARD_COLUMNS = [ | |
| "Nom", | |
| "Date", | |
| "Poids (kg)", | |
| "Vitesse (km/h)", | |
| "Cadence (pas/min)", | |
| "Contact (%)", | |
| "Flight (%)", | |
| "Force talon G (N)", | |
| "Force talon D (N)", | |
| "Force avant-pied G (N)", | |
| "Force avant-pied D (N)", | |
| "Pression talon G (N/cm²)", | |
| "Pression talon D (N/cm²)", | |
| "COP G (mm)", | |
| "COP D (mm)", | |
| "Rotation G (°)", | |
| "Rotation D (°)", | |
| "Transition G (s)", | |
| "Transition D (s)", | |
| "Longueur foulée (cm)", | |
| "Largeur pas (cm)", | |
| ] | |
| def _to_numeric(series): | |
| return pd.to_numeric( | |
| series.astype(str).str.replace(",", ".", regex=False), | |
| errors="coerce" | |
| ) | |
| def _read_csv_flex(uploaded_file): | |
| raw = uploaded_file.read() | |
| uploaded_file.seek(0) | |
| for encoding in ["utf-8-sig", "utf-8", "latin1", "cp1252"]: | |
| for sep in [",", ";", "\t"]: | |
| try: | |
| txt = raw.decode(encoding) | |
| df = pd.read_csv(io.StringIO(txt), sep=sep) | |
| if df.shape[1] > 1: | |
| return df | |
| except Exception: | |
| pass | |
| return pd.read_csv(uploaded_file) | |
| def _pick(df, candidates): | |
| for c in candidates: | |
| if c in df.columns: | |
| return c | |
| return None | |
| def extract_zebris_csv(uploaded_file): | |
| df = _read_csv_flex(uploaded_file) | |
| out = pd.DataFrame(index=df.index) | |
| mapping = {} | |
| manquantes = [] | |
| # Nom | |
| first_name_col = _pick(df, ["Prénom", "First Name"]) | |
| last_name_col = _pick(df, ["Nom de famille", "Last Name"]) | |
| if first_name_col and last_name_col: | |
| out["Nom"] = ( | |
| df[first_name_col].astype(str).str.strip() + " " + | |
| df[last_name_col].astype(str).str.strip() | |
| ) | |
| mapping["Nom"] = [first_name_col, last_name_col] | |
| else: | |
| out["Nom"] = "Inconnu" | |
| manquantes.append("Nom") | |
| # Mapping direct depuis ton CSV Zebris | |
| direct_map = { | |
| "Date": ["Measurement date", "Date"], | |
| "Poids (kg)": ["Body weight [Kg]", "Weight (kg)", "Poids (kg)"], | |
| "Vitesse (km/h)": ["Vitesse [km/h]", "Speed [km/h]", "Speed (km/h)"], | |
| "Cadence (pas/min)": ["Cadence [pass/min]", "Cadence [pas/min]", "Cadence"], | |
| "Contact (%)": ["Total contact [%]", "Contact [%]"], | |
| "Flight (%)": ["Total flight [%]", "Flight [%]"], | |
| "Force talon G (N)": ["Force maximale Heel (Three zones) Gauche [N]"], | |
| "Force talon D (N)": ["Force maximale Heel (Three zones) Droite [N]"], | |
| "Force avant-pied G (N)": ["Force maximale Forefoot (Three zones) Gauche [N]"], | |
| "Force avant-pied D (N)": ["Force maximale Forefoot (Three zones) Droite [N]"], | |
| "Pression talon G (N/cm²)": ["Pression maximale Heel (Three zones) Gauche [N/cm²]", "Pression maximale Heel (Three zones) Gauche [N/cm2]"], | |
| "Pression talon D (N/cm²)": ["Pression maximale Heel (Three zones) Droite [N/cm²]", "Pression maximale Heel (Three zones) Droite [N/cm2]"], | |
| "COP G (mm)": ["Longueur lors de la phase d'appui Gauche [mm]"], | |
| "COP D (mm)": ["Longueur lors de la phase d'appui Droite [mm]"], | |
| "Rotation G (°)": ["Rotation du pied Gauche [degré]"], | |
| "Rotation D (°)": ["Rotation du pied Droite [degré]"], | |
| "Transition G (s)": ["Instant du passage du talon vers l'avant-pied Gauche [s]"], | |
| "Transition D (s)": ["Instant du passage du talon vers l'avant-pied Droite [s]"], | |
| "Longueur foulée (cm)": ["Longueur de la foulée [cm]"], | |
| "Largeur pas (cm)": ["Largeur du pas [cm]"], | |
| } | |
| for target, candidates in direct_map.items(): | |
| col = _pick(df, candidates) | |
| if col is None: | |
| out[target] = np.nan | |
| manquantes.append(target) | |
| else: | |
| mapping[target] = col | |
| if target == "Date": | |
| out[target] = df[col] | |
| else: | |
| out[target] = _to_numeric(df[col]) | |
| # Garde seulement les lignes avec une vitesse | |
| out = out[out["Vitesse (km/h)"].notna()].copy() | |
| # Réordonne | |
| out = out.reindex(columns=STANDARD_COLUMNS).reset_index(drop=True) | |
| debug = { | |
| "mapping": mapping, | |
| "manquantes": manquantes, | |
| "colonnes_csv": list(df.columns), | |
| "nb_lignes_csv": len(df), | |
| "nb_lignes_extractees": len(out), | |
| } | |
| return out, debug |