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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -608
app.py CHANGED
@@ -1,624 +1,95 @@
1
  import streamlit as st
2
- import pandas as pd
3
- import plotly.express as px
4
  import requests
5
- import json
6
- import time
7
- import logging
8
- import datetime
9
- from functools import lru_cache
10
- import base64
11
- from io import StringIO
12
 
13
- # Configuration du logging
14
- logging.basicConfig(level=logging.INFO,
15
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
16
- logger = logging.getLogger('pesticide_explorer')
17
 
18
- # Configuration de la page
19
- st.set_page_config(
20
- page_title="Base de données des pesticides de l'UE",
21
- page_icon="🌱",
22
- layout="wide"
23
- )
 
 
24
 
25
- # Constantes
26
- BASE_URL = "https://api.datalake.sante.service.ec.europa.eu/sante/pesticides"
27
- API_VERSION = "v2.0"
28
- DEFAULT_LANGUAGE = "FR"
29
- DEFAULT_FORMAT = "json"
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,
75
- "language": DEFAULT_LANGUAGE,
76
- "api-version": API_VERSION
77
- }
78
-
79
- all_products = []
80
- next_link = None
81
-
82
- try:
83
- with st.spinner("Chargement de la liste des produits..."):
84
- while True:
85
- if next_link:
86
- response = make_api_request(next_link)
87
- else:
88
- response = make_api_request(url, params)
89
-
90
- if not response:
91
- st.error("Impossible de récupérer la liste des produits. Veuillez réessayer plus tard.")
92
- return pd.DataFrame()
93
-
94
- data = response.json()
95
-
96
- # Récupérer les produits de la page actuelle
97
- products = data.get('value', [])
98
- all_products.extend(products)
99
-
100
- # Vérifier s'il y a une page suivante
101
- next_link = data.get('nextLink') or data.get('@odata.nextLink')
102
- if not next_link:
103
- break
104
-
105
- # Convertir en DataFrame pour faciliter la manipulation
106
- df_products = pd.DataFrame(all_products)
107
-
108
- # Renommer les colonnes pour une utilisation cohérente (gestion des inconsistances de l'API)
109
- column_mapping = {
110
- 'productId': 'product_id',
111
- 'product_Id': 'product_id',
112
- 'productName': 'product_name',
113
- 'product_Name': 'product_name'
114
- }
115
-
116
- df_products = df_products.rename(columns={col: column_mapping[col]
117
- for col in column_mapping
118
- if col in df_products.columns})
119
-
120
- # Tri par nom de produit
121
- if 'product_name' in df_products.columns:
122
- df_products = df_products.sort_values('product_name')
123
-
124
- logger.info(f"Récupération réussie de {len(df_products)} produits")
125
- return df_products
126
-
127
- except Exception as e:
128
- logger.error(f"Erreur lors de la récupération des produits: {str(e)}")
129
- st.error(f"Erreur lors de la récupération des produits: {str(e)}")
130
- return pd.DataFrame()
131
 
132
- @st.cache_data(ttl=3600)
133
- def get_residue_name(residue_id):
134
- """Récupère le nom d'une substance active à partir de son ID."""
135
- url = f"{BASE_URL}/pesticide_residues"
136
- params = {
137
- "format": DEFAULT_FORMAT,
138
- "pesticide_residue_id": residue_id,
139
- "api-version": API_VERSION
140
- }
141
-
142
- try:
143
- response = make_api_request(url, params)
144
- if not response:
145
- logger.warning(f"Impossible de récupérer le nom de la substance {residue_id}")
146
- return f"Substance {residue_id}"
147
-
148
- data = response.json()
149
- substances = data.get('value', [])
150
-
151
- if substances:
152
- # Chercher le nom dans différentes variables possibles
153
- name_fields = ['pesticide_residue_name', 'pesticideResidueName', 'name']
154
- for field in name_fields:
155
- if field in substances[0]:
156
- return substances[0][field]
157
-
158
- # Si aucun champ de nom n'est trouvé
159
- return f"Substance {residue_id}"
160
- else:
161
- return f"Substance {residue_id}"
162
-
163
- except Exception as e:
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,
246
- "product_id": product_id,
247
- "api-version": API_VERSION
248
- }
249
-
250
- all_mrls = []
251
- next_link = None
252
-
253
- try:
254
- with st.spinner("Chargement des LMR..."):
255
- progress_bar = st.progress(0)
256
- pages_processed = 0
257
-
258
- while True:
259
- if next_link:
260
- response = make_api_request(next_link)
261
- else:
262
- response = make_api_request(url, params)
263
-
264
- if not response:
265
- if not all_mrls: # Si aucune donnée n'a été récupérée
266
- st.error("Impossible de récupérer les LMR pour ce produit. Veuillez réessayer plus tard.")
267
- return pd.DataFrame()
268
- else: # Si des données partielles ont été récupérées
269
- st.warning("Récupération partielle des LMR. Certaines données peuvent manquer.")
270
- break
271
-
272
- data = response.json()
273
-
274
- # Récupérer les LMR de la page actuelle
275
- mrls = data.get('value', [])
276
- all_mrls.extend(mrls)
277
-
278
- # Vérifier s'il y a une page suivante
279
- next_link = data.get('nextLink') or data.get('@odata.nextLink')
280
-
281
- # Mettre à jour la barre de progression
282
- pages_processed += 1
283
- progress_bar.progress(min(1.0, pages_processed / 10)) # Estimation de progression
284
-
285
- if not next_link:
286
- progress_bar.progress(1.0)
287
- break
288
-
289
- if not all_mrls:
290
- st.info("Aucune LMR trouvée pour ce produit.")
291
- return pd.DataFrame()
292
-
293
- # Convertir en DataFrame
294
- df_mrls = pd.DataFrame(all_mrls)
295
-
296
- # Renommer les colonnes pour une utilisation cohérente
297
- column_mapping = {
298
- 'pesticideResidueId': 'pesticide_residue_id',
299
- 'pesticide_Residue_Id': 'pesticide_residue_id',
300
- 'mrlValue': 'mrl_value',
301
- 'mrl_Value': 'mrl_value',
302
- 'applicationDate': 'application_date',
303
- 'application_Date': 'application_date',
304
- 'regulationNumber': 'regulation_number',
305
- 'regulation_Number': 'regulation_number'
306
- }
307
-
308
- df_mrls = df_mrls.rename(columns={col: column_mapping[col]
309
- for col in column_mapping
310
- if col in df_mrls.columns})
311
-
312
- # Convertir les dates (gestion de différents formats possibles)
313
- if 'application_date' in df_mrls.columns:
314
- df_mrls['application_date'] = pd.to_datetime(df_mrls['application_date'], errors='coerce')
315
-
316
- # Ajouter le nom de la substance
317
- if 'pesticide_residue_id' in df_mrls.columns:
318
- df_mrls['substance_name'] = df_mrls['pesticide_residue_id'].apply(get_residue_name)
319
-
320
- # Appliquer le filtre temporel si spécifié
321
- if time_filter and 'application_date' in df_mrls.columns:
322
- today = datetime.datetime.now().date()
323
-
324
- if time_filter == 'last_week':
325
- one_week_ago = today - datetime.timedelta(days=7)
326
- df_mrls = df_mrls[df_mrls['application_date'].dt.date >= one_week_ago]
327
-
328
- elif time_filter == 'last_month':
329
- one_month_ago = today - datetime.timedelta(days=30)
330
- df_mrls = df_mrls[df_mrls['application_date'].dt.date >= one_month_ago]
331
-
332
- elif time_filter == 'next_six_months':
333
- six_months_later = today + datetime.timedelta(days=180)
334
- df_mrls = df_mrls[(df_mrls['application_date'].dt.date >= today) &
335
- (df_mrls['application_date'].dt.date <= six_months_later)]
336
-
337
- logger.info(f"Récupération réussie de {len(df_mrls)} LMR pour le produit {product_id}")
338
- return df_mrls
339
-
340
- except Exception as e:
341
- logger.error(f"Erreur lors de la récupération des LMR pour le produit {product_id}: {str(e)}")
342
- st.error(f"Erreur lors de la récupération des LMR: {str(e)}")
343
- return pd.DataFrame()
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
-
371
- except requests.exceptions.RequestException as e:
372
- logger.warning(f"Tentative {attempt+1}/{RETRY_ATTEMPTS} échouée: {str(e)}")
373
-
374
- if attempt < RETRY_ATTEMPTS - 1:
375
- time.sleep(RETRY_DELAY * (attempt + 1)) # Délai progressif
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."""
386
  csv = df.to_csv(index=False)
387
- b64 = base64.b64encode(csv.encode()).decode()
388
- href = f'<a href="data:file/csv;base64,{b64}" download="{filename}">Télécharger les données (CSV)</a>'
389
- return href
390
-
391
- def create_mrls_chart(df_mrls):
392
- """Crée un graphique montrant l'évolution des LMR dans le temps."""
393
- if df_mrls.empty or 'application_date' not in df_mrls.columns or 'mrl_value' not in df_mrls.columns:
394
- return None
395
-
396
- # Sélectionner les 10 substances les plus fréquentes
397
- top_substances = df_mrls['substance_name'].value_counts().nlargest(10).index.tolist()
398
- df_plot = df_mrls[df_mrls['substance_name'].isin(top_substances)]
399
-
400
- # Filtrer les valeurs NaN
401
- df_plot = df_plot.dropna(subset=['application_date', 'mrl_value'])
402
-
403
- if df_plot.empty:
404
- return None
405
-
406
- # Créer le graphique
407
- fig = px.scatter(
408
- df_plot,
409
- x='application_date',
410
- y='mrl_value',
411
- color='substance_name',
412
- hover_name='substance_name',
413
- size_max=10,
414
- title='Évolution des LMR par substance active',
415
- labels={
416
- 'application_date': 'Date d\'application',
417
- 'mrl_value': 'Valeur LMR (mg/kg)',
418
- 'substance_name': 'Substance active'
419
- }
420
- )
421
-
422
- fig.update_layout(
423
- xaxis_title='Date d\'application',
424
- yaxis_title='Valeur LMR (mg/kg)',
425
- legend_title='Substance active',
426
- height=600
427
  )
428
-
429
- return fig
430
-
431
- # Fonction principale de l'application
432
- def main():
433
- # Barre latérale
434
- with st.sidebar:
435
- st.image("https://european-union.europa.eu/sites/default/files/styles/oe_theme_medium_no_crop/public/2021-12/Flag_of_Europe.png", width=150)
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()
464
- st.success("Cache réinitialisé avec succès!")
465
-
466
- # Titre principal
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
544
- col1, col2 = st.columns(2)
545
-
546
- with col1:
547
- # Sélection du produit
548
- product_options = [(row['product_id'], row['product_name'])
549
- for _, row in df_products.iterrows()]
550
- selected_product_id = st.selectbox(
551
- "Sélectionnez un produit agricole",
552
- options=[id for id, _ in product_options],
553
- format_func=lambda x: next((name for id, name in product_options if id == x), str(x))
554
- )
555
-
556
- with col2:
557
- # Filtre temporel
558
- time_filter = st.radio(
559
- "Période",
560
- options=[
561
- ("all", "Toutes dates"),
562
- ("last_week", "Dernière semaine"),
563
- ("last_month", "Dernier mois"),
564
- ("next_six_months", "6 prochains mois")
565
- ],
566
- format_func=lambda x: x[1],
567
- index=0
568
- )
569
-
570
- # Bouton pour lancer l'analyse
571
- if st.button("Analyser les données", type="primary"):
572
- # Récupérer les LMR pour le produit sélectionné
573
- df_mrls = get_mrls_for_product(selected_product_id, time_filter[0] if time_filter[0] != "all" else None)
574
-
575
- if df_mrls.empty:
576
- st.info(f"Aucune donnée LMR trouvée pour ce produit avec les filtres sélectionnés.")
577
- return
578
-
579
- # Afficher les résultats
580
- st.subheader("Limites Maximales de Résidus (LMR)")
581
-
582
- # Formater les données pour l'affichage
583
- display_df = df_mrls.copy()
584
-
585
- # Formater les liens vers les règlements
586
- if 'regulation_number' in display_df.columns:
587
- display_df['regulation_number'] = display_df['regulation_number'].apply(
588
- lambda x: f'<a href="https://eur-lex.europa.eu/search.html?qid=1673437220860&text={x}&scope=EURLEX&type=quick&lang=fr" target="_blank">{x}</a>' if x else "")
589
-
590
- # Sélectionner et renommer les colonnes pour l'affichage
591
- columns_to_display = {
592
- 'substance_name': 'Substance active',
593
- 'mrl_value': 'LMR (mg/kg)',
594
- 'application_date': 'Date d\'application',
595
- 'regulation_number': 'Règlement'
596
- }
597
-
598
- display_df = display_df[[col for col in columns_to_display.keys() if col in display_df.columns]]
599
- display_df = display_df.rename(columns=columns_to_display)
600
-
601
- # Formater la date
602
- if 'Date d\'application' in display_df.columns:
603
- display_df['Date d\'application'] = display_df['Date d\'application'].dt.strftime('%d/%m/%Y')
604
-
605
- # Afficher le tableau avec liens cliquables
606
- st.markdown(display_df.to_html(escape=False, index=False), unsafe_allow_html=True)
607
-
608
- # Afficher le nombre total de LMR
609
- st.info(f"Nombre total de LMR: {len(df_mrls)}")
610
-
611
- # Lien de téléchargement
612
- st.markdown(generate_download_link(df_mrls), unsafe_allow_html=True)
613
-
614
- # Graphique d'évolution des LMR
615
- st.subheader("Évolution des LMR dans le temps")
616
- fig = create_mrls_chart(df_mrls)
617
-
618
- if fig:
619
- st.plotly_chart(fig, use_container_width=True)
620
- else:
621
- st.info("Pas assez de données pour générer un graphique.")
622
-
623
- if __name__ == "__main__":
624
- main()
 
1
  import streamlit as st
 
 
2
  import requests
3
+ import pandas as pd
4
+ import matplotlib.pyplot as plt
5
+ from datetime import datetime, timedelta
 
 
 
 
6
 
7
+ # Configuration de l'application
8
+ st.title("Base de données des pesticides de l'UE")
 
 
9
 
10
+ # Fonction pour récupérer la liste des produits
11
+ @st.cache_data
12
+ def get_products():
13
+ url = "https://api.datalake.sante.service.ec.europa.eu/sante/pesticides/pesticide_residues_products"
14
+ params = {"format": "json", "language": "FR", "api-version": "v2.0"}
15
+ response = requests.get(url, params=params)
16
+ response.raise_for_status()
17
+ return response.json()
18
 
19
+ # Fonction pour récupérer les LMR par produit
20
+ @st.cache_data
21
+ def get_mrls(product_id):
22
+ url = f"https://api.datalake.sante.service.ec.europa.eu/sante/pesticides/pesticide_residues_mrls"
23
+ params = {"format": "json", "product_id": product_id, "api-version": "v2.0"}
24
+ response = requests.get(url, params=params)
25
+ response.raise_for_status()
26
+ return response.json()
27
 
28
+ # Fonction pour récupérer les informations sur les substances
29
+ @st.cache_data
30
+ def get_substance_info(substance_id):
31
+ url = f"https://api.datalake.sante.service.ec.europa.eu/sante/pesticides/pesticide_residues"
32
+ params = {"format": "json", "pesticide_residue_id": substance_id, "api-version": "v2.0"}
33
+ response = requests.get(url, params=params)
34
+ response.raise_for_status()
35
+ return response.json()
 
 
 
 
36
 
37
+ # Interface utilisateur
38
+ products = get_products()
39
+ product_names = [product['name'] for product in products]
40
+ selected_product = st.selectbox("Sélectionnez un produit agricole", product_names)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
+ # Filtrage par période
43
+ period = st.radio("Filtrer par période", ["Toutes dates", "Dernière semaine", "Dernier mois", "6 prochains mois"])
44
+
45
+ # Bouton pour lancer l'analyse
46
+ if st.button("Analyser les données"):
47
+ product_id = next(product['id'] for product in products if product['name'] == selected_product)
48
+ mrls = get_mrls(product_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ # Filtrer les données par période
51
+ today = datetime.today()
52
+ if period == "Dernière semaine":
53
+ start_date = today - timedelta(days=7)
54
+ elif period == "Dernier mois":
55
+ start_date = today - timedelta(days=30)
56
+ elif period == "6 prochains mois":
57
+ start_date = today + timedelta(days=180)
58
+ else:
59
+ start_date = datetime.min
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
+ filtered_mrls = [mrl for mrl in mrls if datetime.strptime(mrl['date'], "%Y-%m-%d") >= start_date]
62
+
63
+ # Afficher les résultats dans un tableau
64
+ mrl_data = []
65
+ for mrl in filtered_mrls:
66
+ substance_info = get_substance_info(mrl['substance_id'])
67
+ mrl_data.append({
68
+ "Substance": substance_info['name'],
69
+ "Valeur LMR": mrl['value'],
70
+ "Date d'application": mrl['date'],
71
+ "Règlement": f"[Lien]({mrl['regulation_url']})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
+ df = pd.DataFrame(mrl_data)
75
+ st.write(df)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ # Visualisation graphique
78
+ plt.figure(figsize=(10, 6))
79
+ for substance in df['Substance'].unique()[:10]: # Limiter aux 10 substances les plus fréquentes
80
+ subset = df[df['Substance'] == substance]
81
+ plt.plot(subset['Date d'application'], subset['Valeur LMR'], label=substance)
82
+ plt.xlabel("Date d'application")
83
+ plt.ylabel("Valeur LMR")
84
+ plt.title("Évolution des LMR dans le temps")
85
+ plt.legend()
86
+ st.pyplot(plt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
+ # Téléchargement CSV
 
89
  csv = df.to_csv(index=False)
90
+ st.download_button(
91
+ label="Télécharger les données en CSV",
92
+ data=csv,
93
+ file_name="lmr_data.csv",
94
+ mime="text/csv",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  )