mays123456 commited on
Commit
acc83b4
·
verified ·
1 Parent(s): 6c79979

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -220
app.py CHANGED
@@ -1,63 +1,40 @@
1
  import gradio as gr
2
  import pandas as pd
3
- import numpy as np
4
  import matplotlib.pyplot as plt
5
 
6
- # --- Mois disponibles, en cohérence avec ton fichier ---
7
- MOIS = ['Janv.', 'Fev.', 'Mars', 'Avr.', 'Mai', 'Juin', 'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Dec.']
8
 
9
- # =========================
10
- # Onglet 1 : ancien graphe
11
- # =========================
12
  def generer_graphique(file, mois):
13
- """
14
- Ancien comportement : charge 'CDT Assemblage' et 'CDT PE' (skiprows=4),
15
- fusionne par 'Standard', calcule la moyenne et colorise selon la valeur.
16
- """
17
- try:
18
- # Lecture des deux feuilles historiques
19
- sheet1 = pd.read_excel(file.name, sheet_name='CDT Assemblage', skiprows=4)
20
- sheet2 = pd.read_excel(file.name, sheet_name='CDT PE', skiprows=4)
21
- except Exception as e:
22
- raise gr.Error(f"Impossible de lire les feuilles 'CDT Assemblage' et 'CDT PE'. Détails: {e}")
23
 
24
- def extraire(df, mois_col):
25
- # 3e colonne = standards dans ta structure existante
26
  standards = df.iloc[:, 2]
27
- if mois_col not in df.columns:
28
- # certains exports peuvent trim/varier les noms (ex: 'Août' -> 'Aout')
29
- # on tente une correspondance souple
30
- candidats = [c for c in df.columns if str(c).strip().lower() == mois_col.strip().lower()]
31
- if not candidats:
32
- raise gr.Error(f"Colonne mois '{mois_col}' introuvable dans la feuille.\nColonnes vues : {list(df.columns)}")
33
- mois_col = candidats[0]
34
- valeurs = df[mois_col]
35
- df_filtre = pd.DataFrame({'Standard': standards, 'Valeur': valeurs}).dropna()
36
  exclure = ['Objectifs', 'Résultats', 'mat>', 'mat> B']
37
- mask = ~df_filtre['Standard'].astype(str).str.contains('|'.join(exclure), case=False, na=False)
38
- return df_filtre[mask]
39
 
40
  df1 = extraire(sheet1, mois)
41
  df2 = extraire(sheet2, mois)
42
  merged = pd.merge(df1, df2, on='Standard', suffixes=('_1', '_2'))
43
  merged['Moyenne'] = merged[['Valeur_1', 'Valeur_2']].mean(axis=1)
44
 
45
- # Respecter l'ordre d'origine des standards de la première feuille
46
  ordre = sheet1.iloc[:, 2].dropna().tolist()
47
- ordre_filtre = [s for s in ordre if s in merged['Standard'].values]
48
- merged = merged.set_index('Standard').loc[ordre_filtre].reset_index()
49
 
50
- # Couleurs par seuils
51
- def couleur(val):
 
52
  if val < 3:
53
- return 'red'
54
  elif val < 6.5:
55
- return 'yellow'
56
  elif val < 9.2:
57
- return 'green'
58
  else:
59
- return 'skyblue'
60
- couleurs = [couleur(v) for v in merged['Moyenne']]
61
 
62
  fig, ax = plt.subplots(figsize=(12, 6))
63
  ax.bar(merged['Standard'], merged['Moyenne'], color=couleurs)
@@ -66,187 +43,18 @@ def generer_graphique(file, mois):
66
  ax.set_ylabel("Moyenne")
67
  plt.xticks(rotation=45, ha='right')
68
  plt.tight_layout()
69
- return fig
70
-
71
- # ===========================================
72
- # Onglet 2 : nouvelles courbes d'évolution
73
- # ===========================================
74
- def _find_header_row(df_raw, mois_labels):
75
- """
76
- Trouve la ligne d'en-tête qui contient un max de libellés de mois.
77
- Retourne l'index de cette ligne ou None.
78
- """
79
- best_idx, best_cnt = None, -1
80
- mois_lower = [m.strip().lower() for m in mois_labels]
81
- for i in range(min(200, len(df_raw))): # pas besoin de balayer toute la feuille
82
- row_vals = [str(x).strip().lower() for x in df_raw.iloc[i, :].tolist()]
83
- cnt = sum(m in row_vals for m in mois_lower)
84
- if cnt > best_cnt:
85
- best_cnt, best_idx = cnt, i
86
- # on considère acceptable si >= 6 mois trouvés
87
- return best_idx if best_cnt >= 6 else None
88
-
89
- def _prep_synthese(df_raw):
90
- """
91
- Nettoie la feuille 'Synthèse usine' en détectant l'en-tête (mois),
92
- puis renvoie (df, mois_trouves, cols_avant_mois)
93
- - df : dataframe propre (lignes de données sous l'en-tête)
94
- - mois_trouves : liste des mois présents dans les colonnes
95
- - cols_avant_mois : noms des colonnes "descriptives" avant les colonnes mois
96
- """
97
- hdr_idx = _find_header_row(df_raw, MOIS)
98
- if hdr_idx is None:
99
- raise gr.Error("Impossible d'identifier l'en-tête des mois dans 'Synthèse usine'.")
100
- header = df_raw.iloc[hdr_idx].tolist()
101
- df = df_raw.iloc[hdr_idx+1:].copy()
102
- df.columns = [str(c).strip() if str(c) != 'nan' else f"col_{i}" for i, c in enumerate(header)]
103
-
104
- # Harmoniser les mois ('Août' vs 'Aout', etc.)
105
- colmap = {}
106
- for c in df.columns:
107
- c_norm = str(c).strip()
108
- if c_norm.lower() == 'aout':
109
- c_norm = 'Août'
110
- colmap[c] = c_norm
111
- df.rename(columns=colmap, inplace=True)
112
-
113
- mois_trouves = [m for m in MOIS if m in df.columns]
114
- if len(mois_trouves) < 6:
115
- raise gr.Error(f"Colonnes mois insuffisantes détectées. Trouvées: {mois_trouves}")
116
-
117
- # Colonnes descriptives = toutes les colonnes avant les mois
118
- first_mois_idx = min(df.columns.get_loc(m) for m in mois_trouves)
119
- cols_avant_mois = df.columns[:first_mois_idx].tolist()
120
- # Nettoyage : forward-fill sur colonnes descriptives (souvent structure en lignes regroupées)
121
- for c in cols_avant_mois:
122
- df[c] = df[c].replace({np.nan: None})
123
- df[c] = df[c].ffill()
124
-
125
- return df, mois_trouves, cols_avant_mois
126
-
127
- def _pick_best_column_for(labels_df, candidates, keywords):
128
- """
129
- Parmi les colonnes 'candidates', trouve celle qui matche le mieux 'keywords'
130
- (tous les mots-clés doivent apparaître, insensibles à la casse).
131
- """
132
- best_col, best_hits = None, -1
133
- kws = [k.lower() for k in keywords]
134
- for c in candidates:
135
- vals = labels_df[c].astype(str).str.lower()
136
- hits = vals.apply(lambda x: all(k in x for k in kws)).sum()
137
- if hits > best_hits:
138
- best_hits, best_col = hits, c
139
- return best_col
140
-
141
- def _extract_series(df, mois_cols, cols_avant_mois, kpi_keywords, type_keywords):
142
- """
143
- Extrait une série (sur 12 mois) pour un couple (KPI, TYPE).
144
- - kpi_keywords : ex. ['cdt', 'assemblage']
145
- - type_keywords : ex. ['obj'] ou ['act'] (gère 'act'/'act.')
146
- Retourne un pd.Series indexé par mois_cols (peut contenir NaN).
147
- """
148
- # On cherche sur les colonnes descriptives où se trouvent KPI et TYPE
149
- kpi_col = _pick_best_column_for(df, cols_avant_mois, kpi_keywords)
150
- type_col = _pick_best_column_for(df, cols_avant_mois, type_keywords)
151
-
152
- if kpi_col is None:
153
- raise gr.Error(f"Impossible de localiser le KPI {' '.join(kpi_keywords)} dans 'Synthèse usine'.")
154
- if type_col is None:
155
- # parfois 'Obj/Act' est dans la même colonne que le KPI ou dans une 2e colonne voisine
156
- # on tente alors dans kpi_col
157
- type_col = kpi_col
158
-
159
- # Filtrage
160
- mask_kpi = df[kpi_col].astype(str).str.lower().apply(lambda x: all(k in x for k in [k.lower() for k in kpi_keywords]))
161
- # gérer 'act' ou 'act.' et 'obj'
162
- type_norm = [t.lower().rstrip('.') for t in type_keywords]
163
- mask_type = df[type_col].astype(str).str.lower().apply(lambda x: any(t in x.rstrip('.') for t in type_norm))
164
-
165
- sub = df[mask_kpi & mask_type]
166
- if sub.empty:
167
- # fallback: si pas trouvé, tenter recherche large en concaténant les colonnes descriptives
168
- concat_desc = df[cols_avant_mois].astype(str).agg(' '.join, axis=1).str.lower()
169
- mask_all = concat_desc.apply(lambda x: all(k in x for k in [k.lower() for k in kpi_keywords])) & \
170
- concat_desc.apply(lambda x: any(t in x for t in type_norm))
171
- sub = df[mask_all]
172
-
173
- if sub.empty:
174
- # on renvoie une série NaN pour ne pas casser le tracé global
175
- return pd.Series([np.nan]*len(mois_cols), index=mois_cols, dtype=float)
176
 
177
- # Si plusieurs lignes matchent, on prend la première non vide sur les mois (ou la moyenne)
178
- # Ici, on prend la moyenne par mois pour être robuste
179
- series_vals = sub[mois_cols].apply(pd.to_numeric, errors='coerce').mean(axis=0)
180
- return series_vals
181
-
182
- def courbes_evolution(file):
183
- """
184
- Nouvelle fonctionnalité : lit 'Synthèse usine' -> tableau '#@mat B AOS par regroupement de ligne'
185
- puis trace 6 courbes :
186
- - CDT Assemblage (OBJ, ACT)
187
- - CDT PE (OBJ, ACT)
188
- - Moyenne OBJ (entre les 2 OBJ)
189
- - Moyenne ACT (entre les 2 ACT)
190
- """
191
- try:
192
- df_raw = pd.read_excel(file.name, sheet_name='Synthèse usine', header=None)
193
- except Exception as e:
194
- raise gr.Error(f"Impossible de lire la feuille 'Synthèse usine'. Détails: {e}")
195
-
196
- df, mois_cols, cols_desc = _prep_synthese(df_raw)
197
-
198
- # Extraire les 4 séries principales
199
- asm_obj = _extract_series(df, mois_cols, cols_desc, kpi_keywords=['cdt', 'assemblage'], type_keywords=['obj'])
200
- asm_act = _extract_series(df, mois_cols, cols_desc, kpi_keywords=['cdt', 'assemblage'], type_keywords=['act'])
201
- pe_obj = _extract_series(df, mois_cols, cols_desc, kpi_keywords=['cdt', 'pe'], type_keywords=['obj'])
202
- pe_act = _extract_series(df, mois_cols, cols_desc, kpi_keywords=['cdt', 'pe'], type_keywords=['act'])
203
-
204
- # Moyennes OBJ/ACT entre Assemblage et PE
205
- mean_obj = pd.concat([asm_obj, pe_obj], axis=1).mean(axis=1)
206
- mean_act = pd.concat([asm_act, pe_act], axis=1).mean(axis=1)
207
-
208
- # Tracé
209
- fig, ax = plt.subplots(figsize=(12, 6))
210
- x = np.arange(len(mois_cols))
211
-
212
- # 4 courbes + 2 moyennes
213
- ax.plot(x, asm_obj.values, marker='o', label='CDT Assemblage - OBJ')
214
- ax.plot(x, asm_act.values, marker='o', label='CDT Assemblage - ACT')
215
- ax.plot(x, pe_obj.values, marker='o', label='CDT PE - OBJ')
216
- ax.plot(x, pe_act.values, marker='o', label='CDT PE - ACT')
217
- ax.plot(x, mean_obj.values, marker='D', linestyle='--', label='Moyenne OBJ (Asm+PE)')
218
- ax.plot(x, mean_act.values, marker='D', linestyle='--', label='Moyenne ACT (Asm+PE)')
219
-
220
- ax.set_xticks(x)
221
- ax.set_xticklabels(mois_cols, rotation=0)
222
- ax.set_xlabel("Mois")
223
- ax.set_ylabel("Valeur")
224
- ax.set_title("Évolution mensuelle – CDT Assemblage & CDT PE (OBJ/ACT) + Moyennes")
225
- ax.grid(True, which='both', linestyle=':', linewidth=0.8)
226
- ax.legend(loc='best', ncol=2)
227
- plt.tight_layout()
228
  return fig
229
 
230
- # ======================
231
- # Interface Gradio (UI)
232
- # ======================
233
- with gr.Blocks(title="Maturity Analysis Dashboard") as demo:
234
- gr.Markdown("## Analyse de Maturité\nChargez votre fichier Excel et utilisez les onglets ci-dessous.")
235
- with gr.Tabs():
236
- with gr.Tab("Par standard (ancien)"):
237
- in_file1 = gr.File(label="Fichier Excel (.xlsx)", file_types=[".xlsx"])
238
- mois_dd = gr.Dropdown(choices=MOIS, value="Janv.", label="Mois")
239
- out_plot1 = gr.Plot(label="Graphique")
240
- btn1 = gr.Button("Générer")
241
- btn1.click(fn=generer_graphique, inputs=[in_file1, mois_dd], outputs=out_plot1)
242
-
243
- with gr.Tab("Courbes d’évolution (nouveau)"):
244
- in_file2 = gr.File(label="Fichier Excel (.xlsx)", file_types=[".xlsx"])
245
- out_plot2 = gr.Plot(label="Évolution mensuelle")
246
- btn2 = gr.Button("Tracer")
247
- btn2.click(fn=courbes_evolution, inputs=[in_file2], outputs=out_plot2)
248
-
249
- # Pour HF Spaces / Docker : ne pas ouvrir de navigateur
250
- if __name__ == "__main__":
251
- demo.launch(server_name="0.0.0.0", server_port=7860)
252
-
 
1
  import gradio as gr
2
  import pandas as pd
 
3
  import matplotlib.pyplot as plt
4
 
5
+ mois_disponibles = ['Janv.', 'Fev.', 'Mars', 'Avr.', 'Mai', 'Juin', 'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Dec.']
 
6
 
 
 
 
7
  def generer_graphique(file, mois):
8
+ sheet1 = pd.read_excel(file.name, sheet_name='CDT Assemblage', skiprows=4)
9
+ sheet2 = pd.read_excel(file.name, sheet_name='CDT PE', skiprows=4)
 
 
 
 
 
 
 
 
10
 
11
+ def extraire(df, mois):
 
12
  standards = df.iloc[:, 2]
13
+ valeurs = df[mois]
14
+ df_filtré = pd.DataFrame({'Standard': standards, 'Valeur': valeurs}).dropna()
 
 
 
 
 
 
 
15
  exclure = ['Objectifs', 'Résultats', 'mat>', 'mat> B']
16
+ return df_filtré[~df_filtré['Standard'].astype(str).str.contains('|'.join(exclure), case=False)]
 
17
 
18
  df1 = extraire(sheet1, mois)
19
  df2 = extraire(sheet2, mois)
20
  merged = pd.merge(df1, df2, on='Standard', suffixes=('_1', '_2'))
21
  merged['Moyenne'] = merged[['Valeur_1', 'Valeur_2']].mean(axis=1)
22
 
 
23
  ordre = sheet1.iloc[:, 2].dropna().tolist()
24
+ ordre_filtré = [s for s in ordre if s in merged['Standard'].values]
25
+ merged = merged.set_index('Standard').loc[ordre_filtré].reset_index()
26
 
27
+ # Définir les couleurs selon les valeurs
28
+ couleurs = []
29
+ for val in merged['Moyenne']:
30
  if val < 3:
31
+ couleurs.append('red')
32
  elif val < 6.5:
33
+ couleurs.append('yellow')
34
  elif val < 9.2:
35
+ couleurs.append('green')
36
  else:
37
+ couleurs.append('skyblue')
 
38
 
39
  fig, ax = plt.subplots(figsize=(12, 6))
40
  ax.bar(merged['Standard'], merged['Moyenne'], color=couleurs)
 
43
  ax.set_ylabel("Moyenne")
44
  plt.xticks(rotation=45, ha='right')
45
  plt.tight_layout()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  return fig
48
 
49
+ interface = gr.Interface(
50
+ fn=generer_graphique,
51
+ inputs=[
52
+ gr.File(label="Fichier Excel (.xlsx)", file_types=[".xlsx"]),
53
+ gr.Dropdown(choices=mois_disponibles, label="Mois")
54
+ ],
55
+ outputs=gr.Plot(label="Graphique"),
56
+ title="Analyse de Maturité par Standard",
57
+ description="Chargez un fichier Excel, sélectionnez un mois, et visualisez la moyenne des scores."
58
+ )
59
+
60
+ interface.launch()