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