MMOON commited on
Commit
34e3a4a
·
verified ·
1 Parent(s): 5817584

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -45
app.py CHANGED
@@ -32,12 +32,27 @@ st.info("Les données sont chargées depuis la Plateforme SCA. Utilisez les filt
32
  # 📦 Fonction pour charger les données avec cache
33
  @st.cache_data(ttl=3600) # Mettre en cache les données pendant 1 heure
34
  def load_data():
35
- file_url = "https://www.plateforme-sca.fr/sites/default/files/2025-11/BuSCA-Base2.xlsx"
 
 
36
  try:
 
37
  response = requests.get(file_url, headers={'User-Agent': 'Mozilla/5.0'})
38
  response.raise_for_status()
 
39
  df = pd.read_excel(BytesIO(response.content), engine='openpyxl')
 
 
40
  df.columns = df.columns.str.strip().str.lower()
 
 
 
 
 
 
 
 
 
41
  return df
42
  except Exception as e:
43
  st.error(f"Erreur critique lors du chargement des données : {e}")
@@ -50,11 +65,11 @@ if df_full is None:
50
  st.error("Impossible de continuer car les données n'ont pas pu être chargées.")
51
  st.stop()
52
 
53
- # --- Définition des noms de colonnes ---
54
  COL_BUSCA = 'busca'
55
  COL_TITRE = 'titre'
56
- COL_MATRICE = 'matrices'
57
- COL_DANGER = 'dangers'
58
  COL_SECTION = 'section'
59
  COL_TEXTE = 'texte'
60
  COL_LIEN1 = 'lien'
@@ -64,9 +79,15 @@ COL_LIEN2 = 'lien2'
64
  essential_cols = [COL_BUSCA, COL_TITRE, COL_TEXTE, COL_MATRICE, COL_DANGER]
65
  missing_cols = [col for col in essential_cols if col not in df_full.columns]
66
  if missing_cols:
67
- st.error(f"ERREUR : Les colonnes essentielles suivantes sont manquantes : {', '.join(missing_cols)}")
 
68
  st.stop()
69
 
 
 
 
 
 
70
  # Tri des données
71
  df_full = df_full.sort_values(by=COL_BUSCA, ascending=False)
72
 
@@ -76,7 +97,7 @@ with st.sidebar:
76
 
77
  if st.button("🔄 Rafraîchir les données"):
78
  st.cache_data.clear()
79
- st.experimental_rerun()
80
 
81
  with st.expander("📌 Plage de numéros de BuSCA", expanded=True):
82
  min_val = int(df_full[COL_BUSCA].min())
@@ -84,14 +105,15 @@ with st.sidebar:
84
  busca_range = st.slider("Numéros de BuSCA", min_val, max_val, (max_val - 20, max_val))
85
 
86
  with st.expander("🌍 Matrices"):
87
- unique_matrices = sorted(df_full[COL_MATRICE].dropna().unique())
 
88
  matrices = st.multiselect("Sélectionner les matrices", options=unique_matrices)
89
 
90
  with st.expander("⚠️ Dangers"):
91
- unique_dangers = sorted(df_full[COL_DANGER].dropna().unique())
92
  dangers = st.multiselect("Sélectionner les dangers", options=unique_dangers)
93
 
94
- # --- AJOUT DU FILTRE TEXTE LIBRE ---
95
  with st.expander("🔎 Recherche par mots-clés"):
96
  keywords = st.text_area("Mots-clés (séparés par des virgules)", placeholder="ex: listeria, lait, rappel...")
97
 
@@ -99,37 +121,40 @@ with st.sidebar:
99
 
100
  # Logique de filtrage
101
  df_display = df_full.copy()
102
- if apply_filter:
103
- with st.spinner('Filtrage en cours...'):
104
- # Filtre par plage de BuSCA
105
- df_display = df_display[
106
- (df_display[COL_BUSCA] >= busca_range[0]) &
107
- (df_display[COL_BUSCA] <= busca_range[1])
108
- ]
109
- # Filtre par listes
110
- if matrices:
111
- df_display = df_display[df_display[COL_MATRICE].isin(matrices)]
112
- if dangers:
113
- df_display = df_display[df_display[COL_DANGER].isin(dangers)]
114
-
115
- # --- AJOUT DE LA LOGIQUE DE FILTRAGE PAR MOTS-CLÉS ---
116
- if keywords:
117
- # Prépare la liste de mots-clés : minuscule, sans espaces superflus
118
- keyword_list = [kw.strip().lower() for kw in keywords.split(',') if kw.strip()]
119
-
120
- # Applique le filtre si des mots-clés ont été saisis
121
- if keyword_list:
122
- # La fonction cherche si AU MOINS UN des mots-clés est présent dans la ligne
123
- df_display = df_display[df_display.apply(
124
- lambda row: any(
125
- kw in str(row[COL_TITRE]).lower() or
126
- kw in str(row[COL_TEXTE]).lower() or
127
- kw in str(row[COL_DANGER]).lower() or
128
- kw in str(row[COL_MATRICE]).lower()
129
- for kw in keyword_list
130
- ),
131
- axis=1
132
- )]
 
 
 
133
 
134
  # Affichage des résultats
135
  st.markdown(f"### 📑 Affichage de {len(df_display)} résultats")
@@ -146,9 +171,15 @@ else:
146
 
147
  st.markdown(f"**Danger :** `{danger_val}` | **Matrice :** `{matrice_val}`")
148
  st.markdown("---")
149
- st.markdown(str(row.get(COL_TEXTE, 'Texte manquant')))
 
 
150
 
151
- if pd.notna(row.get(COL_LIEN1)):
152
- st.markdown(f"🔗 [Lien 1]({row[COL_LIEN1]})")
153
- if pd.notna(row.get(COL_LIEN2)):
154
- st.markdown(f"🔗 [Lien 2]({row[COL_LIEN2]})")
 
 
 
 
 
32
  # 📦 Fonction pour charger les données avec cache
33
  @st.cache_data(ttl=3600) # Mettre en cache les données pendant 1 heure
34
  def load_data():
35
+ # --- MODIFICATION DE L'URL ICI ---
36
+ file_url = "https://www.plateforme-sca.fr/media/398/download"
37
+
38
  try:
39
+ # En Python (côté serveur), pas besoin de proxy CORS, mais on garde le User-Agent
40
  response = requests.get(file_url, headers={'User-Agent': 'Mozilla/5.0'})
41
  response.raise_for_status()
42
+
43
  df = pd.read_excel(BytesIO(response.content), engine='openpyxl')
44
+
45
+ # Nettoyage des noms de colonnes (minuscules, sans espaces)
46
  df.columns = df.columns.str.strip().str.lower()
47
+
48
+ # --- ROBUSTESSE : Renommage pour gérer Singulier/Pluriel ---
49
+ # Si le fichier contient "matrice" au lieu de "matrices", on normalise
50
+ rename_map = {
51
+ 'matrice': 'matrices',
52
+ 'danger': 'dangers'
53
+ }
54
+ df.rename(columns=rename_map, inplace=True)
55
+
56
  return df
57
  except Exception as e:
58
  st.error(f"Erreur critique lors du chargement des données : {e}")
 
65
  st.error("Impossible de continuer car les données n'ont pas pu être chargées.")
66
  st.stop()
67
 
68
+ # --- Définition des noms de colonnes (Normalisés) ---
69
  COL_BUSCA = 'busca'
70
  COL_TITRE = 'titre'
71
+ COL_MATRICE = 'matrices' # On utilise le pluriel car on a normalisé ci-dessus
72
+ COL_DANGER = 'dangers' # On utilise le pluriel car on a normalisé ci-dessus
73
  COL_SECTION = 'section'
74
  COL_TEXTE = 'texte'
75
  COL_LIEN1 = 'lien'
 
79
  essential_cols = [COL_BUSCA, COL_TITRE, COL_TEXTE, COL_MATRICE, COL_DANGER]
80
  missing_cols = [col for col in essential_cols if col not in df_full.columns]
81
  if missing_cols:
82
+ st.error(f"ERREUR : Les colonnes essentielles suivantes sont manquantes dans le fichier Excel : {', '.join(missing_cols)}")
83
+ st.write("Colonnes trouvées :", df_full.columns.tolist())
84
  st.stop()
85
 
86
+ # Nettoyage des données (suppression des lignes sans N° BuSCA)
87
+ df_full = df_full.dropna(subset=[COL_BUSCA])
88
+ # Conversion du N° BuSCA en entier
89
+ df_full[COL_BUSCA] = df_full[COL_BUSCA].astype(int)
90
+
91
  # Tri des données
92
  df_full = df_full.sort_values(by=COL_BUSCA, ascending=False)
93
 
 
97
 
98
  if st.button("🔄 Rafraîchir les données"):
99
  st.cache_data.clear()
100
+ st.rerun() # Utilisation de st.rerun() au lieu de experimental_rerun
101
 
102
  with st.expander("📌 Plage de numéros de BuSCA", expanded=True):
103
  min_val = int(df_full[COL_BUSCA].min())
 
105
  busca_range = st.slider("Numéros de BuSCA", min_val, max_val, (max_val - 20, max_val))
106
 
107
  with st.expander("🌍 Matrices"):
108
+ # Conversion en string pour éviter les erreurs de tri si données mixtes
109
+ unique_matrices = sorted(df_full[COL_MATRICE].fillna('Non spécifié').astype(str).unique())
110
  matrices = st.multiselect("Sélectionner les matrices", options=unique_matrices)
111
 
112
  with st.expander("⚠️ Dangers"):
113
+ unique_dangers = sorted(df_full[COL_DANGER].fillna('Non spécifié').astype(str).unique())
114
  dangers = st.multiselect("Sélectionner les dangers", options=unique_dangers)
115
 
116
+ # --- FILTRE TEXTE LIBRE ---
117
  with st.expander("🔎 Recherche par mots-clés"):
118
  keywords = st.text_area("Mots-clés (séparés par des virgules)", placeholder="ex: listeria, lait, rappel...")
119
 
 
121
 
122
  # Logique de filtrage
123
  df_display = df_full.copy()
124
+
125
+ # Note: Streamlit relance le script à chaque interaction, donc si on n'appuie pas sur le bouton
126
+ # on affiche quand même les données filtrées par défaut (tout ou dernière action).
127
+ # Si vous voulez que rien ne change tant qu'on ne clique pas, il faut gérer le state,
128
+ # mais ici on applique la logique standard :
129
+ if apply_filter or True: # 'or True' permet un affichage dynamique réactif immédiat (optionnel selon préférence UX)
130
+ # Filtre par plage de BuSCA
131
+ df_display = df_display[
132
+ (df_display[COL_BUSCA] >= busca_range[0]) &
133
+ (df_display[COL_BUSCA] <= busca_range[1])
134
+ ]
135
+ # Filtre par listes
136
+ if matrices:
137
+ df_display = df_display[df_display[COL_MATRICE].astype(str).isin(matrices)]
138
+ if dangers:
139
+ df_display = df_display[df_display[COL_DANGER].astype(str).isin(dangers)]
140
+
141
+ # --- LOGIQUE DE FILTRAGE PAR MOTS-CLÉS ---
142
+ if keywords:
143
+ # Prépare la liste de mots-clés : minuscule, sans espaces superflus
144
+ keyword_list = [kw.strip().lower() for kw in keywords.split(',') if kw.strip()]
145
+
146
+ # Applique le filtre si des mots-clés ont été saisis
147
+ if keyword_list:
148
+ df_display = df_display[df_display.apply(
149
+ lambda row: any(
150
+ kw in str(row[COL_TITRE]).lower() or
151
+ kw in str(row[COL_TEXTE]).lower() or
152
+ kw in str(row[COL_DANGER]).lower() or
153
+ kw in str(row[COL_MATRICE]).lower()
154
+ for kw in keyword_list
155
+ ),
156
+ axis=1
157
+ )]
158
 
159
  # Affichage des résultats
160
  st.markdown(f"### 📑 Affichage de {len(df_display)} résultats")
 
171
 
172
  st.markdown(f"**Danger :** `{danger_val}` | **Matrice :** `{matrice_val}`")
173
  st.markdown("---")
174
+ # Remplacement des sauts de ligne pour un affichage propre
175
+ texte_content = str(row.get(COL_TEXTE, 'Texte manquant')).replace('\n', ' \n')
176
+ st.markdown(texte_content)
177
 
178
+ st.markdown("---")
179
+ col1, col2 = st.columns(2)
180
+ with col1:
181
+ if pd.notna(row.get(COL_LIEN1)):
182
+ st.link_button("🔗 Lien Principal", row[COL_LIEN1])
183
+ with col2:
184
+ if pd.notna(row.get(COL_LIEN2)):
185
+ st.link_button("🔗 Lien Secondaire", row[COL_LIEN2])