Spaces:
Sleeping
Sleeping
File size: 4,370 Bytes
cc55ec2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 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 |