MMOON commited on
Commit
3adb96e
·
verified ·
1 Parent(s): b5df1bd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -5
app.py CHANGED
@@ -30,10 +30,45 @@ DEFAULT_FORMAT = "json"
30
  RETRY_ATTEMPTS = 3
31
  RETRY_DELAY = 2 # secondes
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  # Cache pour les données
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  @st.cache_data(ttl=3600) # Cache d'une heure
35
  def get_products():
36
  """Récupère la liste complète des produits agricoles."""
 
 
 
 
37
  url = f"{BASE_URL}/pesticide_residues_products"
38
  params = {
39
  "format": DEFAULT_FORMAT,
@@ -129,9 +164,82 @@ def get_residue_name(residue_id):
129
  logger.error(f"Erreur lors de la récupération du nom de la substance {residue_id}: {str(e)}")
130
  return f"Substance {residue_id}"
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  @st.cache_data(ttl=1800) # Cache de 30 minutes
133
  def get_mrls_for_product(product_id, time_filter=None):
134
  """Récupère les LMR pour un produit spécifique avec gestion de la pagination."""
 
 
 
 
135
  url = f"{BASE_URL}/pesticide_residues_mrls"
136
  params = {
137
  "format": DEFAULT_FORMAT,
@@ -236,9 +344,27 @@ def get_mrls_for_product(product_id, time_filter=None):
236
 
237
  def make_api_request(url, params=None):
238
  """Effectue une requête API avec gestion des erreurs et tentatives."""
 
 
 
 
 
 
 
 
 
 
 
 
239
  for attempt in range(RETRY_ATTEMPTS):
240
  try:
241
- response = requests.get(url, params=params)
 
 
 
 
 
 
242
  response.raise_for_status()
243
  return response
244
 
@@ -250,6 +376,10 @@ def make_api_request(url, params=None):
250
  else:
251
  logger.error(f"Échec de la requête après {RETRY_ATTEMPTS} tentatives: {url}")
252
  return None
 
 
 
 
253
 
254
  def generate_download_link(df, filename="pesticide_data.csv"):
255
  """Génère un lien de téléchargement pour un DataFrame."""
@@ -306,6 +436,28 @@ def main():
306
  st.title("Pesticide Data Explorer")
307
  st.markdown("Application pour explorer les données de pesticides de l'Union Européenne.")
308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  # Bouton de réinitialisation du cache
310
  if st.button("Réinitialiser le cache"):
311
  st.cache_data.clear()
@@ -315,11 +467,77 @@ def main():
315
  st.title("Base de données des pesticides de l'UE")
316
  st.markdown("Explorez les Limites Maximales de Résidus (LMR) par produit agricole")
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  # Récupération de la liste des produits
319
  df_products = get_products()
320
 
321
- if df_products.empty:
322
- st.warning("Aucun produit disponible. Veuillez vérifier la connexion à l'API.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  return
324
 
325
  # Interface de sélection
@@ -403,5 +621,4 @@ def main():
403
  st.info("Pas assez de données pour générer un graphique.")
404
 
405
  if __name__ == "__main__":
406
- main()
407
-
 
30
  RETRY_ATTEMPTS = 3
31
  RETRY_DELAY = 2 # secondes
32
 
33
+ # Paramètres d'authentification
34
+ def get_api_key():
35
+ """Récupère la clé API depuis les secrets Streamlit ou l'interface utilisateur."""
36
+ # Essayer de récupérer depuis les secrets Streamlit
37
+ try:
38
+ return st.secrets["eu_pesticides_api_key"]
39
+ except:
40
+ # Si pas disponible dans les secrets, demander à l'utilisateur
41
+ if "api_key" not in st.session_state:
42
+ st.session_state.api_key = ""
43
+
44
+ return st.session_state.api_key
45
+
46
  # Cache pour les données
47
+ # Fonction pour charger des données de démonstration
48
+ def load_demo_data():
49
+ """Charge des données de démonstration pour montrer les fonctionnalités de l'application."""
50
+ # Produits de démonstration
51
+ products_data = [
52
+ {"product_id": 1, "product_name": "Pommes", "product_code": "0130010"},
53
+ {"product_id": 2, "product_name": "Poires", "product_code": "0130020"},
54
+ {"product_id": 3, "product_name": "Tomates", "product_code": "0231010"},
55
+ {"product_id": 4, "product_name": "Raisins", "product_code": "0151010"},
56
+ {"product_id": 5, "product_name": "Oranges", "product_code": "0110010"},
57
+ {"product_id": 6, "product_name": "Blé", "product_code": "0500010"},
58
+ {"product_id": 7, "product_name": "Riz", "product_code": "0500060"},
59
+ {"product_id": 8, "product_name": "Carottes", "product_code": "0213020"},
60
+ ]
61
+
62
+ # Convertir en DataFrame
63
+ return pd.DataFrame(products_data)
64
+
65
  @st.cache_data(ttl=3600) # Cache d'une heure
66
  def get_products():
67
  """Récupère la liste complète des produits agricoles."""
68
+ # Vérifier si le mode démo est activé
69
+ if hasattr(st.session_state, 'demo_mode') and st.session_state.demo_mode:
70
+ return load_demo_data()
71
+
72
  url = f"{BASE_URL}/pesticide_residues_products"
73
  params = {
74
  "format": DEFAULT_FORMAT,
 
164
  logger.error(f"Erreur lors de la récupération du nom de la substance {residue_id}: {str(e)}")
165
  return f"Substance {residue_id}"
166
 
167
+ # Fonction pour charger des LMR de démonstration
168
+ def load_demo_mrls(product_id, time_filter=None):
169
+ """Charge des LMR de démonstration pour le produit sélectionné."""
170
+ # Substances de démonstration
171
+ substances = {
172
+ 1: "Glyphosate",
173
+ 2: "Clothianidine",
174
+ 3: "Pendiméthaline",
175
+ 4: "Imidaclopride",
176
+ 5: "Thiamethoxam",
177
+ 6: "Lambda-cyhalothrine",
178
+ 7: "Acetamipride"
179
+ }
180
+
181
+ # Générer des données aléatoires
182
+ import random
183
+
184
+ # Date de base
185
+ base_date = datetime.datetime.now().date()
186
+
187
+ # Générer entre 20 et 40 entrées aléatoires
188
+ num_entries = random.randint(20, 40)
189
+
190
+ mrls_data = []
191
+
192
+ for _ in range(num_entries):
193
+ # Choisir une substance aléatoire
194
+ substance_id = random.choice(list(substances.keys()))
195
+
196
+ # Générer une date aléatoire dans les 3 dernières années
197
+ days_offset = random.randint(-1095, 180) # -3 ans à +6 mois
198
+ random_date = base_date + datetime.timedelta(days=days_offset)
199
+
200
+ # Générer une valeur LMR aléatoire (entre 0.01 et 5.0)
201
+ mrl_value = round(random.uniform(0.01, 5.0), 2)
202
+
203
+ # Règlement fictif
204
+ regulation = f"{random.randint(2020, 2024)}/{random.randint(100, 999)}"
205
+
206
+ mrls_data.append({
207
+ "pesticide_residue_id": substance_id,
208
+ "substance_name": substances[substance_id],
209
+ "mrl_value": mrl_value,
210
+ "application_date": pd.to_datetime(random_date),
211
+ "regulation_number": regulation
212
+ })
213
+
214
+ # Convertir en DataFrame
215
+ df_mrls = pd.DataFrame(mrls_data)
216
+
217
+ # Appliquer le filtre temporel si spécifié
218
+ if time_filter:
219
+ today = datetime.datetime.now().date()
220
+
221
+ if time_filter == 'last_week':
222
+ one_week_ago = today - datetime.timedelta(days=7)
223
+ df_mrls = df_mrls[df_mrls['application_date'].dt.date >= one_week_ago]
224
+
225
+ elif time_filter == 'last_month':
226
+ one_month_ago = today - datetime.timedelta(days=30)
227
+ df_mrls = df_mrls[df_mrls['application_date'].dt.date >= one_month_ago]
228
+
229
+ elif time_filter == 'next_six_months':
230
+ six_months_later = today + datetime.timedelta(days=180)
231
+ df_mrls = df_mrls[(df_mrls['application_date'].dt.date >= today) &
232
+ (df_mrls['application_date'].dt.date <= six_months_later)]
233
+
234
+ return df_mrls
235
+
236
  @st.cache_data(ttl=1800) # Cache de 30 minutes
237
  def get_mrls_for_product(product_id, time_filter=None):
238
  """Récupère les LMR pour un produit spécifique avec gestion de la pagination."""
239
+ # Vérifier si le mode démo est activé
240
+ if hasattr(st.session_state, 'demo_mode') and st.session_state.demo_mode:
241
+ return load_demo_mrls(product_id, time_filter)
242
+
243
  url = f"{BASE_URL}/pesticide_residues_mrls"
244
  params = {
245
  "format": DEFAULT_FORMAT,
 
344
 
345
  def make_api_request(url, params=None):
346
  """Effectue une requête API avec gestion des erreurs et tentatives."""
347
+ # Récupérer la clé API
348
+ api_key = get_api_key()
349
+
350
+ # Ajouter les en-têtes d'authentification si une clé API est disponible
351
+ headers = {}
352
+ if api_key:
353
+ headers["Authorization"] = f"Bearer {api_key}"
354
+ # Selon l'API, il peut y avoir d'autres formats d'authentification comme :
355
+ # headers["X-API-Key"] = api_key
356
+ # ou
357
+ # headers["Ocp-Apim-Subscription-Key"] = api_key
358
+
359
  for attempt in range(RETRY_ATTEMPTS):
360
  try:
361
+ response = requests.get(url, params=params, headers=headers)
362
+
363
+ # Si nous obtenons une erreur 403 et que nous n'avons pas de clé API,
364
+ # lever une exception spécifique
365
+ if response.status_code == 403 and not api_key:
366
+ raise Exception("Authentification requise pour accéder à l'API")
367
+
368
  response.raise_for_status()
369
  return response
370
 
 
376
  else:
377
  logger.error(f"Échec de la requête après {RETRY_ATTEMPTS} tentatives: {url}")
378
  return None
379
+
380
+ except Exception as e:
381
+ logger.error(f"Erreur: {str(e)}")
382
+ return None
383
 
384
  def generate_download_link(df, filename="pesticide_data.csv"):
385
  """Génère un lien de téléchargement pour un DataFrame."""
 
436
  st.title("Pesticide Data Explorer")
437
  st.markdown("Application pour explorer les données de pesticides de l'Union Européenne.")
438
 
439
+ # Section d'authentification
440
+ st.subheader("Authentification API")
441
+ api_key = st.text_input("Clé API (obligatoire)",
442
+ type="password",
443
+ help="Entrez votre clé API pour accéder aux données")
444
+ st.session_state.api_key = api_key
445
+
446
+ st.info("""
447
+ Pour obtenir une clé API, inscrivez-vous sur le portail européen des données
448
+ et demandez l'accès à l'API des pesticides.
449
+ """)
450
+
451
+ # Mode démonstration
452
+ st.subheader("Mode démonstration")
453
+ use_demo = st.checkbox("Utiliser des données de démonstration",
454
+ help="Activez cette option pour utiliser des données fictives et tester l'application sans clé API")
455
+ if use_demo:
456
+ st.session_state.demo_mode = True
457
+ st.success("Mode démonstration activé!")
458
+ else:
459
+ st.session_state.demo_mode = False
460
+
461
  # Bouton de réinitialisation du cache
462
  if st.button("Réinitialiser le cache"):
463
  st.cache_data.clear()
 
467
  st.title("Base de données des pesticides de l'UE")
468
  st.markdown("Explorez les Limites Maximales de Résidus (LMR) par produit agricole")
469
 
470
+ # Vérifier si le mode démo est activé ou si une clé API est fournie
471
+ if not ((hasattr(st.session_state, 'demo_mode') and st.session_state.demo_mode) or get_api_key()):
472
+ st.error("""
473
+ ⚠️ Authentification requise!
474
+
475
+ Cette API nécessite une clé d'accès. Veuillez soit:
476
+ 1. Entrer votre clé API dans la barre latérale pour accéder aux données réelles, ou
477
+ 2. Activer le "Mode démonstration" pour utiliser des données fictives.
478
+
479
+ Erreur actuelle: 403 Forbidden - L'accès à l'API est refusé sans authentification valide.
480
+ """)
481
+
482
+ # Afficher une section d'aide pour obtenir une clé API
483
+ col1, col2 = st.columns(2)
484
+
485
+ with col1:
486
+ st.subheader("Option 1: Obtenir une clé API")
487
+ st.markdown("""
488
+ 1. Rendez-vous sur le [Portail des données de l'UE](https://data.europa.eu/)
489
+ 2. Créez un compte ou connectez-vous
490
+ 3. Naviguez vers la section API des pesticides
491
+ 4. Demandez une clé d'accès pour l'API
492
+ 5. Copiez la clé dans le champ "Clé API" dans la barre latérale
493
+ """)
494
+
495
+ with col2:
496
+ st.subheader("Option 2: Mode démonstration")
497
+ st.markdown("""
498
+ 1. Activez l'option "Utiliser des données de démonstration" dans la barre latérale
499
+ 2. Utilisez l'application avec des données fictives pour tester les fonctionnalités
500
+ 3. Notez que les données affichées ne seront pas réelles
501
+ """)
502
+
503
+ if st.button("Activer le mode démonstration"):
504
+ st.session_state.demo_mode = True
505
+ st.experimental_rerun()
506
+
507
+ return
508
+
509
+ # Afficher un message si en mode démonstration
510
+ if hasattr(st.session_state, 'demo_mode') and st.session_state.demo_mode:
511
+ st.warning("""
512
+ 🔍 **Mode démonstration activé**
513
+
514
+ Vous utilisez des données fictives pour tester l'application.
515
+ Les valeurs affichées ne correspondent pas aux données réelles de l'API de l'UE.
516
+
517
+ Pour accéder aux données réelles, désactivez le mode démonstration et fournissez une clé API valide.
518
+ """)
519
+
520
  # Récupération de la liste des produits
521
  df_products = get_products()
522
 
523
+ if df_products.empty and not (hasattr(st.session_state, 'demo_mode') and st.session_state.demo_mode):
524
+ st.warning("Aucun produit disponible. Veuillez vérifier votre clé API ou la connexion à l'API.")
525
+
526
+ # Ajouter des options alternatives
527
+ st.subheader("Options alternatives")
528
+ st.markdown("""
529
+ Si vous rencontrez des difficultés pour accéder à l'API en direct, vous pouvez:
530
+
531
+ 1. **Activer le mode démonstration** dans la barre latérale pour tester les fonctionnalités
532
+ 2. Accéder directement aux données via le site web de l'UE:
533
+ - [Base de données des pesticides de l'UE](https://ec.europa.eu/food/plant/pesticides/eu-pesticides-database/products/)
534
+ - [Téléchargement direct des fichiers CSV](https://ec.europa.eu/food/plant/pesticides/eu-pesticides-database/products/download/)
535
+ """)
536
+
537
+ if st.button("Activer le mode démonstration", key="demo_btn_alt"):
538
+ st.session_state.demo_mode = True
539
+ st.experimental_rerun()
540
+
541
  return
542
 
543
  # Interface de sélection
 
621
  st.info("Pas assez de données pour générer un graphique.")
622
 
623
  if __name__ == "__main__":
624
+ main()