MMOON commited on
Commit
9847cf9
·
verified ·
1 Parent(s): b8b6b12

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -53
app.py CHANGED
@@ -39,7 +39,9 @@ class PesticideDataFetcher:
39
  HEADERS = {
40
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
41
  'Accept': 'application/json',
42
- 'Accept-Language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7'
 
 
43
  }
44
  CACHE_DIR = "cache"
45
  SUBSTANCE_CACHE_FILE = os.path.join(CACHE_DIR, "substance_cache.json")
@@ -62,10 +64,12 @@ class PesticideDataFetcher:
62
  if use_cache:
63
  self._load_caches()
64
 
65
- # Préchargement des substances si le cache est vide
66
  if not self._substance_cache:
67
  self.preload_substance_names()
68
 
 
 
69
  def _load_caches(self):
70
  """Charge les données de cache depuis les fichiers"""
71
  try:
@@ -123,10 +127,10 @@ class PesticideDataFetcher:
123
  logger.error(f"Erreur lors de la sauvegarde des caches: {e}")
124
 
125
  @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
126
- def fetch_data(self, url: str) -> Dict[str, Any]:
127
- """Récupère les données depuis l'API avec mécanisme de retry"""
128
  try:
129
- response = self.session.get(url, timeout=15)
130
  response.raise_for_status()
131
  data = response.json()
132
  logger.debug(f"Réponse API pour {url}: {str(data)[:200]}...")
@@ -138,46 +142,60 @@ class PesticideDataFetcher:
138
  def preload_substance_names(self) -> None:
139
  """Précharge tous les noms de substances depuis l'API"""
140
  logger.info("Préchargement des substances...")
141
- url = f"{self.BASE_URL}/pesticide_residues?format=json&language=FR&api-version=v2.0"
 
142
  substances_loaded = 0
143
 
144
- while url:
145
- data = self.fetch_data(url)
146
- if "error" in data:
147
- logger.error(f"Erreur lors du préchargement des substances: {data.get('error', 'Erreur inconnue')}")
148
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
- for substance in data.get("value", []):
151
- substance_id = substance.get("pesticideResidueId")
152
- if substance_id and substance_id not in self._substance_cache:
153
- self._substance_cache[substance_id] = SubstanceDetails(
154
- name=substance.get("pesticideResidueName", "Inconnu"),
155
- substance_id=substance_id,
156
- status=substance.get("status"),
157
- cas_number=substance.get("casNumber"),
158
- ec_number=substance.get("ecNumber")
159
- )
160
- substances_loaded += 1
161
 
162
- # Chargement des détails supplémentaires pour chaque substance
163
- self._load_substance_details(substance_id)
 
 
 
164
 
165
- url = data.get("nextLink")
166
- logger.info(f"Substances chargées jusqu'à présent: {substances_loaded}")
167
 
168
- if self.use_cache:
169
- self._save_caches()
170
 
171
- logger.info(f"Préchargement terminé. Total des substances: {len(self._substance_cache)}")
 
 
 
172
 
173
  def _load_substance_details(self, substance_id: int) -> None:
174
  """Charge les détails supplémentaires d'une substance"""
175
- url = f"{self.BASE_URL}/pesticide_residues/{substance_id}?format=json&language=FR&api-version=v2.0"
 
176
  try:
177
- data = self.fetch_data(url)
178
 
179
  substance = self._substance_cache.get(substance_id)
180
- if substance:
181
  substance.approval_date = data.get("approvalDate")
182
  substance.expiry_date = data.get("expiryDate")
183
  # Mise à jour du statut si non défini précédemment
@@ -193,8 +211,11 @@ class PesticideDataFetcher:
193
 
194
  # Si la substance n'est pas dans le cache, essayer de la récupérer
195
  try:
196
- url = f"{self.BASE_URL}/pesticide_residues/{substance_id}?format=json&language=FR&api-version=v2.0"
197
- data = self.fetch_data(url)
 
 
 
198
 
199
  substance_name = data.get("pesticideResidueName", f"Substance {substance_id}")
200
 
@@ -223,23 +244,28 @@ class PesticideDataFetcher:
223
  return list(self._product_cache.values())
224
 
225
  logger.info("Récupération de la liste des produits...")
226
- url = f"{self.BASE_URL}/pesticide_residues_products?format=json&language=FR&api-version=v2.0"
 
227
  products_loaded = 0
228
 
229
  while url:
230
- data = self.fetch_data(url)
231
  if "error" in data:
232
  logger.error(f"Erreur produits: {data.get('error', 'Aucune info')}")
233
  break
234
 
235
- for product in data.get("value", []):
236
- product_id = product.get("productId")
237
- if product_id:
238
- self._product_cache[product_id] = product
239
- products_loaded += 1
 
240
 
241
  url = data.get("nextLink")
242
  logger.info(f"Produits récupérés jusqu'à présent: {products_loaded}")
 
 
 
243
 
244
  if self.use_cache:
245
  self._save_caches()
@@ -254,17 +280,21 @@ class PesticideDataFetcher:
254
  return self._mrl_cache[product_id]
255
 
256
  logger.info(f"Récupération des LMR pour le produit {product_id}...")
257
- url = f"{self.BASE_URL}/pesticide_residues_products/{product_id}/mrls?format=json&language=FR&api-version=v2.0"
 
258
 
259
  mrls = []
260
  while url:
261
- data = self.fetch_data(url)
262
  if "error" in data:
263
  logger.error(f"Erreur lors de la récupération des LMR: {data.get('error', 'Aucune info')}")
264
  break
265
-
266
- mrls.extend(data.get("value", []))
267
  url = data.get("nextLink")
 
 
 
268
 
269
  # Mise à jour du cache
270
  self._mrl_cache[product_id] = mrls
@@ -284,20 +314,31 @@ class PesticideDataFetcher:
284
  ]
285
  return sorted(results, key=lambda x: x.name)
286
 
287
- def get_substance_mrls(self, substance_id: int) -> List[Dict[str,Any]]: #Changed List to Dict
288
- """Récupère tous les produits avec LMR pour une substance donnée"""
289
  logger.info(f"Récupération des LMR pour la substance {substance_id}...")
290
- url = f"{self.BASE_URL}/pesticide_residues/{substance_id}/mrls?format=json&language=FR&api-version=v2.0"
 
 
 
 
 
 
291
 
292
  all_mrls = []
293
  while url:
294
- data = self.fetch_data(url)
295
  if "error" in data:
296
  logger.error(f"Erreur lors de la récupération des LMR: {data.get('error', 'Aucune info')}")
297
  break
298
 
299
- all_mrls.extend(data.get("value", []))
 
300
  url = data.get("nextLink")
 
 
 
 
301
 
302
  logger.info(f"LMR récupérées pour la substance {substance_id}: {len(all_mrls)}")
303
  return all_mrls
@@ -429,7 +470,7 @@ class PesticideApp:
429
 
430
  df = pd.DataFrame(data) # Crée le DataFrame
431
  logger.info(f"LMR récupérées pour {substance.name}: {len(df)} entrées")
432
- return df # MODIFICATION: retourner le dataframe
433
 
434
  def create_histogram(self, df: pd.DataFrame) -> go.Figure:
435
  """Crée un histogramme des valeurs LMR"""
@@ -574,8 +615,8 @@ class PesticideApp:
574
 
575
  substance_mrls_btn.click(
576
  fn=lambda sid: (
577
- self.get_substance_mrls(int(sid)), #MODIFICATION: int() conversion
578
- self.create_histogram(self.get_substance_mrls(int(sid))) #MODIFICATION : int() conversion
579
  ),
580
  inputs=[substance_select],
581
  outputs=[substance_mrls, mrl_histogram]
 
39
  HEADERS = {
40
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
41
  'Accept': 'application/json',
42
+ 'Accept-Language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7',
43
+ "Content-Type": "application/json", # Added as per API docs
44
+ "Cache-Control": "no-cache" # Added as per API docs
45
  }
46
  CACHE_DIR = "cache"
47
  SUBSTANCE_CACHE_FILE = os.path.join(CACHE_DIR, "substance_cache.json")
 
64
  if use_cache:
65
  self._load_caches()
66
 
67
+ # Préchargement des substances si le cache est vide et si le chargement réussi.
68
  if not self._substance_cache:
69
  self.preload_substance_names()
70
 
71
+
72
+
73
  def _load_caches(self):
74
  """Charge les données de cache depuis les fichiers"""
75
  try:
 
127
  logger.error(f"Erreur lors de la sauvegarde des caches: {e}")
128
 
129
  @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
130
+ def fetch_data(self, url: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
131
+ """Récupère les données depuis l'API avec mécanisme de retry et paramètres optionnels."""
132
  try:
133
+ response = self.session.get(url, params=params, timeout=15)
134
  response.raise_for_status()
135
  data = response.json()
136
  logger.debug(f"Réponse API pour {url}: {str(data)[:200]}...")
 
142
  def preload_substance_names(self) -> None:
143
  """Précharge tous les noms de substances depuis l'API"""
144
  logger.info("Préchargement des substances...")
145
+ url = f"{self.BASE_URL}/pesticide_residues" # Corrected endpoint
146
+ params = {"format": "json", "language": "FR", "api-version": "v1.0"} # Use params dict
147
  substances_loaded = 0
148
 
149
+ try:
150
+ data = self.fetch_data(url, params=params) # Initial fetch outside the loop
151
+ if not data or "value" not in data: # Handle potential empty or malformed response
152
+ logger.warning("Initial fetch returned no data. Skipping preloading.")
153
+ return
154
+
155
+ while url:
156
+ if "error" in data:
157
+ logger.error(f"Erreur lors du préchargement des substances: {data.get('error', 'Erreur inconnue')}")
158
+ break
159
+
160
+ for substance in data.get("value", []):
161
+ substance_id = substance.get("pesticideResidueId")
162
+ if substance_id and substance_id not in self._substance_cache:
163
+ self._substance_cache[substance_id] = SubstanceDetails(
164
+ name=substance.get("pesticideResidueName", "Inconnu"),
165
+ substance_id=substance_id,
166
+ status=substance.get("status"),
167
+ cas_number=substance.get("casNumber"),
168
+ ec_number=substance.get("ecNumber")
169
+ )
170
+ substances_loaded += 1
171
 
172
+ # Chargement des détails supplémentaires pour chaque substance
173
+ self._load_substance_details(substance_id)
 
 
 
 
 
 
 
 
 
174
 
175
+ url = data.get("nextLink")
176
+ logger.info(f"Substances chargées jusqu'à présent: {substances_loaded}")
177
+ if url: # Only fetch if there's a nextLink
178
+ # No need to pass params again for nextLink, the API handles it
179
+ data = self.fetch_data(url)
180
 
 
 
181
 
182
+ if self.use_cache:
183
+ self._save_caches()
184
 
185
+ logger.info(f"Préchargement terminé. Total des substances: {len(self._substance_cache)}")
186
+ except Exception as e:
187
+ logger.error(f"Erreur lors du préchargement des substances: {e}")
188
+ return # Exit gracefully
189
 
190
  def _load_substance_details(self, substance_id: int) -> None:
191
  """Charge les détails supplémentaires d'une substance"""
192
+ url = f"{self.BASE_URL}/pesticide_residues/{substance_id}" # Corrected endpoint
193
+ params = {"format": "json", "language": "FR", "api-version": "v1.0"} # Use params dict
194
  try:
195
+ data = self.fetch_data(url, params=params)
196
 
197
  substance = self._substance_cache.get(substance_id)
198
+ if substance and data: #check also if data is valid
199
  substance.approval_date = data.get("approvalDate")
200
  substance.expiry_date = data.get("expiryDate")
201
  # Mise à jour du statut si non défini précédemment
 
211
 
212
  # Si la substance n'est pas dans le cache, essayer de la récupérer
213
  try:
214
+ url = f"{self.BASE_URL}/pesticide_residues/{substance_id}" # Corrected endpoint
215
+ params = {"format": "json", "language": "FR", "api-version": "v1.0"} # Use params dict
216
+ data = self.fetch_data(url, params)
217
+ if not data:
218
+ return f"Substance inconnue ({substance_id})"
219
 
220
  substance_name = data.get("pesticideResidueName", f"Substance {substance_id}")
221
 
 
244
  return list(self._product_cache.values())
245
 
246
  logger.info("Récupération de la liste des produits...")
247
+ url = f"{self.BASE_URL}/pesticide_residues_products" # Corrected endpoint
248
+ params = {"format": "json", "language": "FR", "api-version": "v1.0"} # Use params dict
249
  products_loaded = 0
250
 
251
  while url:
252
+ data = self.fetch_data(url, params=params)
253
  if "error" in data:
254
  logger.error(f"Erreur produits: {data.get('error', 'Aucune info')}")
255
  break
256
 
257
+ if data:
258
+ for product in data.get("value", []):
259
+ product_id = product.get("productId")
260
+ if product_id:
261
+ self._product_cache[product_id] = product
262
+ products_loaded += 1
263
 
264
  url = data.get("nextLink")
265
  logger.info(f"Produits récupérés jusqu'à présent: {products_loaded}")
266
+ if url :
267
+ # No need to pass params again for nextLink
268
+ data = self.fetch_data(url)
269
 
270
  if self.use_cache:
271
  self._save_caches()
 
280
  return self._mrl_cache[product_id]
281
 
282
  logger.info(f"Récupération des LMR pour le produit {product_id}...")
283
+ url = f"{self.BASE_URL}/pesticide_residues_products/{product_id}/mrls" # Corrected endpoint
284
+ params = {"format": "json", "language": "FR", "api-version": "v1.0"} # Use params dict
285
 
286
  mrls = []
287
  while url:
288
+ data = self.fetch_data(url, params=params)
289
  if "error" in data:
290
  logger.error(f"Erreur lors de la récupération des LMR: {data.get('error', 'Aucune info')}")
291
  break
292
+ if data:
293
+ mrls.extend(data.get("value", []))
294
  url = data.get("nextLink")
295
+ if url:
296
+ # No need for params in nextLink
297
+ data = self.fetch_data(url)
298
 
299
  # Mise à jour du cache
300
  self._mrl_cache[product_id] = mrls
 
314
  ]
315
  return sorted(results, key=lambda x: x.name)
316
 
317
+ def get_substance_mrls(self, substance_id: int) -> List[Dict[str, Any]]:
318
+ """Récupère tous les produits avec LMR pour une substance donnée en utilisant le nouvel endpoint."""
319
  logger.info(f"Récupération des LMR pour la substance {substance_id}...")
320
+
321
+ url = f"{self.BASE_URL}/pesticide_residues_mrls" # Correct endpoint
322
+ params = {
323
+ "format": "json",
324
+ "api-version": "v1.0",
325
+ "pesticide_residue_id": substance_id # Use the correct parameter name
326
+ }
327
 
328
  all_mrls = []
329
  while url:
330
+ data = self.fetch_data(url, params=params) # Pass params here
331
  if "error" in data:
332
  logger.error(f"Erreur lors de la récupération des LMR: {data.get('error', 'Aucune info')}")
333
  break
334
 
335
+ if data:
336
+ all_mrls.extend(data.get("value", []))
337
  url = data.get("nextLink")
338
+ if url:
339
+ # No need to pass params again for the nextLink.
340
+ data = self.fetch_data(url)
341
+
342
 
343
  logger.info(f"LMR récupérées pour la substance {substance_id}: {len(all_mrls)}")
344
  return all_mrls
 
470
 
471
  df = pd.DataFrame(data) # Crée le DataFrame
472
  logger.info(f"LMR récupérées pour {substance.name}: {len(df)} entrées")
473
+ return df
474
 
475
  def create_histogram(self, df: pd.DataFrame) -> go.Figure:
476
  """Crée un histogramme des valeurs LMR"""
 
615
 
616
  substance_mrls_btn.click(
617
  fn=lambda sid: (
618
+ self.get_substance_mrls(int(sid)),
619
+ self.create_histogram(self.get_substance_mrls(int(sid)))
620
  ),
621
  inputs=[substance_select],
622
  outputs=[substance_mrls, mrl_histogram]