Spaces:
Sleeping
Sleeping
Tracy André
commited on
Commit
·
27281c3
1
Parent(s):
676811f
updated
Browse files- analyzer.py +137 -53
- data_loader.py +66 -41
- interface.py +152 -57
- main.py +72 -6
- main_fallback.py +147 -0
- visualizations.py +317 -145
analyzer.py
CHANGED
|
@@ -191,77 +191,161 @@ class AgricultureAnalyzer:
|
|
| 191 |
return risk_analysis.sort_values(['Risk_Score', 'IFT_herbicide_approx'])
|
| 192 |
|
| 193 |
def get_summary_stats(self):
|
| 194 |
-
"""Retourne les statistiques de résumé"""
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
## 📊 Statistiques Générales
|
| 200 |
-
- **Nombre total de parcelles**: {
|
| 201 |
-
- **Nombre d'interventions**: {
|
| 202 |
-
- **Surface totale**: {
|
| 203 |
-
- **Surface moyenne par parcelle**: {
|
| 204 |
-
- **Période**: {
|
| 205 |
|
| 206 |
## 🧪 Analyse Herbicides
|
| 207 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
|
| 209 |
-
|
| 210 |
-
herbicides_df = self.df[self.df['familleprod'] == 'Herbicides']
|
| 211 |
-
if len(herbicides_df) > 0:
|
| 212 |
-
stats_text += f"""
|
| 213 |
-
- **Interventions herbicides**: {len(herbicides_df)} ({(len(herbicides_df)/len(self.df)*100):.1f}%)
|
| 214 |
-
- **Parcelles traitées**: {herbicides_df['numparcell'].nunique()}
|
| 215 |
-
- **Produits herbicides différents**: {herbicides_df['produit'].nunique()}
|
| 216 |
"""
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
|
| 222 |
## 🎯 Répartition des Risques Adventices
|
| 223 |
"""
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
|
| 232 |
def get_low_risk_recommendations(self):
|
| 233 |
-
"""Retourne les recommandations pour les parcelles à faible risque"""
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
low_risk = self.risk_analysis[
|
| 238 |
-
self.risk_analysis['Risque_adventice'].isin(['TRÈS FAIBLE', 'FAIBLE'])
|
| 239 |
-
].head(10)
|
| 240 |
-
|
| 241 |
-
recommendations = "## 🌾 TOP 10 - Parcelles Recommandées pour Cultures Sensibles (Pois, Haricot)\n\n"
|
| 242 |
-
|
| 243 |
-
for idx, row in low_risk.iterrows():
|
| 244 |
-
if isinstance(idx, tuple) and len(idx) >= 4:
|
| 245 |
-
parcelle, nom, culture, surface = idx[:4]
|
| 246 |
-
else:
|
| 247 |
-
# Fallback si l'index n'est pas un tuple de 4 éléments
|
| 248 |
-
parcelle = str(idx)
|
| 249 |
-
nom = "N/A"
|
| 250 |
-
culture = "N/A"
|
| 251 |
-
surface = row.get('surfparc', 0) if 'surfparc' in row else 0
|
| 252 |
|
| 253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
**Parcelle {parcelle}** ({nom})
|
| 255 |
- Culture actuelle: {culture}
|
| 256 |
- Surface: {surface:.2f} ha
|
| 257 |
-
- Niveau de risque: {
|
| 258 |
-
- IFT herbicide: {
|
| 259 |
-
- Nombre d'herbicides: {
|
| 260 |
|
| 261 |
---
|
| 262 |
"""
|
| 263 |
-
|
| 264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
def get_risk_analysis(self):
|
| 267 |
"""Retourne l'analyse des risques"""
|
|
|
|
| 191 |
return risk_analysis.sort_values(['Risk_Score', 'IFT_herbicide_approx'])
|
| 192 |
|
| 193 |
def get_summary_stats(self):
|
| 194 |
+
"""Retourne les statistiques de résumé avec gestion d'erreur"""
|
| 195 |
+
try:
|
| 196 |
+
if self.df is None:
|
| 197 |
+
return "❌ Aucune donnée disponible"
|
| 198 |
+
|
| 199 |
+
# Statistiques générales avec gestion d'erreur
|
| 200 |
+
try:
|
| 201 |
+
total_parcelles = self.df['numparcell'].nunique()
|
| 202 |
+
total_interventions = len(self.df)
|
| 203 |
+
surface_totale = self.df['surfparc'].sum()
|
| 204 |
+
surface_moyenne = self.df['surfparc'].mean()
|
| 205 |
+
periode_min = self.df['millesime'].min()
|
| 206 |
+
periode_max = self.df['millesime'].max()
|
| 207 |
+
|
| 208 |
+
stats_text = f"""
|
| 209 |
## 📊 Statistiques Générales
|
| 210 |
+
- **Nombre total de parcelles**: {total_parcelles}
|
| 211 |
+
- **Nombre d'interventions**: {total_interventions:,}
|
| 212 |
+
- **Surface totale**: {surface_totale:.2f} hectares
|
| 213 |
+
- **Surface moyenne par parcelle**: {surface_moyenne:.2f} hectares
|
| 214 |
+
- **Période**: {periode_min} - {periode_max}
|
| 215 |
|
| 216 |
## 🧪 Analyse Herbicides
|
| 217 |
"""
|
| 218 |
+
except Exception as e:
|
| 219 |
+
print(f"❌ Erreur dans les statistiques générales: {e}")
|
| 220 |
+
stats_text = """
|
| 221 |
+
## 📊 Statistiques Générales
|
| 222 |
+
❌ Erreur lors du calcul des statistiques générales
|
| 223 |
|
| 224 |
+
## 🧪 Analyse Herbicides
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
"""
|
| 226 |
+
|
| 227 |
+
# Analyse des herbicides avec gestion d'erreur
|
| 228 |
+
try:
|
| 229 |
+
if 'familleprod' in self.df.columns:
|
| 230 |
+
herbicides_df = self.df[self.df['familleprod'] == 'Herbicides']
|
| 231 |
+
if len(herbicides_df) > 0:
|
| 232 |
+
nb_herbicides = len(herbicides_df)
|
| 233 |
+
pct_herbicides = (nb_herbicides/len(self.df)*100)
|
| 234 |
+
parcelles_traitees = herbicides_df['numparcell'].nunique()
|
| 235 |
+
|
| 236 |
+
if 'produit' in herbicides_df.columns:
|
| 237 |
+
produits_uniques = herbicides_df['produit'].nunique()
|
| 238 |
+
stats_text += f"""
|
| 239 |
+
- **Interventions herbicides**: {nb_herbicides} ({pct_herbicides:.1f}%)
|
| 240 |
+
- **Parcelles traitées**: {parcelles_traitees}
|
| 241 |
+
- **Produits herbicides différents**: {produits_uniques}
|
| 242 |
+
"""
|
| 243 |
+
else:
|
| 244 |
+
stats_text += f"""
|
| 245 |
+
- **Interventions herbicides**: {nb_herbicides} ({pct_herbicides:.1f}%)
|
| 246 |
+
- **Parcelles traitées**: {parcelles_traitees}
|
| 247 |
+
"""
|
| 248 |
+
else:
|
| 249 |
+
stats_text += "\n- **Aucune intervention herbicide détectée**"
|
| 250 |
+
else:
|
| 251 |
+
stats_text += "\n- **Données d'herbicides non disponibles**"
|
| 252 |
+
except Exception as e:
|
| 253 |
+
print(f"❌ Erreur dans l'analyse des herbicides: {e}")
|
| 254 |
+
stats_text += "\n❌ Erreur lors de l'analyse des herbicides"
|
| 255 |
+
|
| 256 |
+
# Analyse des risques avec gestion d'erreur
|
| 257 |
+
try:
|
| 258 |
+
if self.risk_analysis is not None and len(self.risk_analysis) > 0:
|
| 259 |
+
risk_distribution = self.risk_analysis['Risque_adventice'].value_counts()
|
| 260 |
+
stats_text += f"""
|
| 261 |
|
| 262 |
## 🎯 Répartition des Risques Adventices
|
| 263 |
"""
|
| 264 |
+
for risk_level in RISK_LEVELS:
|
| 265 |
+
if risk_level in risk_distribution:
|
| 266 |
+
count = risk_distribution[risk_level]
|
| 267 |
+
pct = (count / len(self.risk_analysis)) * 100
|
| 268 |
+
stats_text += f"- **{risk_level}**: {count} parcelles ({pct:.1f}%)\n"
|
| 269 |
+
else:
|
| 270 |
+
stats_text += "\n\n❌ Analyse des risques non disponible"
|
| 271 |
+
except Exception as e:
|
| 272 |
+
print(f"❌ Erreur dans l'analyse des risques: {e}")
|
| 273 |
+
stats_text += "\n\n❌ Erreur lors de l'analyse des risques"
|
| 274 |
+
|
| 275 |
+
return stats_text
|
| 276 |
+
|
| 277 |
+
except Exception as e:
|
| 278 |
+
print(f"❌ Erreur critique dans get_summary_stats: {e}")
|
| 279 |
+
return "❌ Erreur critique lors de la génération des statistiques"
|
| 280 |
|
| 281 |
def get_low_risk_recommendations(self):
|
| 282 |
+
"""Retourne les recommandations pour les parcelles à faible risque avec gestion d'erreur"""
|
| 283 |
+
try:
|
| 284 |
+
if self.risk_analysis is None or len(self.risk_analysis) == 0:
|
| 285 |
+
return "❌ Analyse des risques non disponible"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
|
| 287 |
+
try:
|
| 288 |
+
low_risk = self.risk_analysis[
|
| 289 |
+
self.risk_analysis['Risque_adventice'].isin(['TRÈS FAIBLE', 'FAIBLE'])
|
| 290 |
+
].head(10)
|
| 291 |
+
|
| 292 |
+
if len(low_risk) == 0:
|
| 293 |
+
return """## 🌾 Recommandations pour Cultures Sensibles
|
| 294 |
+
|
| 295 |
+
❌ Aucune parcelle à faible risque trouvée.
|
| 296 |
+
|
| 297 |
+
💡 **Suggestion**: Considérez une rotation plus longue ou des techniques alternatives pour réduire la pression adventice."""
|
| 298 |
+
|
| 299 |
+
recommendations = "## 🌾 TOP 10 - Parcelles Recommandées pour Cultures Sensibles (Pois, Haricot)\n\n"
|
| 300 |
+
|
| 301 |
+
for idx, row in low_risk.iterrows():
|
| 302 |
+
try:
|
| 303 |
+
if isinstance(idx, tuple) and len(idx) >= 4:
|
| 304 |
+
parcelle, nom, culture, surface = idx[:4]
|
| 305 |
+
else:
|
| 306 |
+
# Fallback si l'index n'est pas un tuple de 4 éléments
|
| 307 |
+
parcelle = str(idx)
|
| 308 |
+
nom = "N/A"
|
| 309 |
+
culture = "N/A"
|
| 310 |
+
surface = row.get('surfparc', 0) if hasattr(row, 'get') else 0
|
| 311 |
+
|
| 312 |
+
# Vérification des valeurs avec fallbacks
|
| 313 |
+
risque = row.get('Risque_adventice', 'N/A') if hasattr(row, 'get') else 'N/A'
|
| 314 |
+
ift = row.get('IFT_herbicide_approx', 0) if hasattr(row, 'get') else 0
|
| 315 |
+
nb_herb = row.get('Nb_herbicides', 0) if hasattr(row, 'get') else 0
|
| 316 |
+
|
| 317 |
+
recommendations += f"""
|
| 318 |
**Parcelle {parcelle}** ({nom})
|
| 319 |
- Culture actuelle: {culture}
|
| 320 |
- Surface: {surface:.2f} ha
|
| 321 |
+
- Niveau de risque: {risque}
|
| 322 |
+
- IFT herbicide: {ift:.2f}
|
| 323 |
+
- Nombre d'herbicides: {nb_herb}
|
| 324 |
|
| 325 |
---
|
| 326 |
"""
|
| 327 |
+
except Exception as e:
|
| 328 |
+
print(f"❌ Erreur lors du traitement d'une parcelle: {e}")
|
| 329 |
+
recommendations += f"""
|
| 330 |
+
**Parcelle {str(idx)}**
|
| 331 |
+
❌ Erreur lors du traitement des données de cette parcelle
|
| 332 |
+
|
| 333 |
+
---
|
| 334 |
+
"""
|
| 335 |
+
|
| 336 |
+
return recommendations
|
| 337 |
+
|
| 338 |
+
except Exception as e:
|
| 339 |
+
print(f"❌ Erreur lors de la génération des recommandations: {e}")
|
| 340 |
+
return """## 🌾 Recommandations pour Cultures Sensibles
|
| 341 |
+
|
| 342 |
+
❌ Erreur lors de la génération des recommandations.
|
| 343 |
+
|
| 344 |
+
💡 **Suggestion**: Vérifiez la qualité des données et relancez l'analyse."""
|
| 345 |
+
|
| 346 |
+
except Exception as e:
|
| 347 |
+
print(f"❌ Erreur critique dans get_low_risk_recommendations: {e}")
|
| 348 |
+
return "❌ Erreur critique lors de la génération des recommandations"
|
| 349 |
|
| 350 |
def get_risk_analysis(self):
|
| 351 |
"""Retourne l'analyse des risques"""
|
data_loader.py
CHANGED
|
@@ -15,53 +15,78 @@ class DataLoader:
|
|
| 15 |
self.df = None
|
| 16 |
|
| 17 |
def load_data(self):
|
| 18 |
-
"""Charge les données du dataset Hugging Face"""
|
| 19 |
-
print(MESSAGES["loading"])
|
| 20 |
-
print(f"📋 Dataset ID: {DATASET_ID}")
|
| 21 |
-
print(f"📋 Token disponible: {'Oui' if HF_TOKEN else 'Non'}")
|
| 22 |
-
|
| 23 |
-
self.df = None
|
| 24 |
-
|
| 25 |
-
# 1) Tentative de chargement direct via datasets.load_dataset
|
| 26 |
try:
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
token=HF_TOKEN,
|
| 31 |
-
trust_remote_code=True,
|
| 32 |
-
)
|
| 33 |
-
print(f"📊 Dataset chargé: {len(dataset)} exemples")
|
| 34 |
|
|
|
|
|
|
|
|
|
|
| 35 |
try:
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
if self.df is None:
|
| 54 |
-
|
|
|
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
return MESSAGES["no_data"]
|
| 59 |
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
def _clean_and_validate_data(self):
|
| 67 |
"""Nettoie et valide les données chargées"""
|
|
|
|
| 15 |
self.df = None
|
| 16 |
|
| 17 |
def load_data(self):
|
| 18 |
+
"""Charge les données du dataset Hugging Face avec gestion d'erreur robuste"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
try:
|
| 20 |
+
print(MESSAGES["loading"])
|
| 21 |
+
print(f"📋 Dataset ID: {DATASET_ID}")
|
| 22 |
+
print(f"📋 Token disponible: {'Oui' if HF_TOKEN else 'Non'}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
self.df = None
|
| 25 |
+
|
| 26 |
+
# 1) Tentative de chargement direct via datasets.load_dataset
|
| 27 |
try:
|
| 28 |
+
print("🔄 Tentative de chargement direct...")
|
| 29 |
+
dataset = load_dataset(
|
| 30 |
+
DATASET_ID,
|
| 31 |
+
split="train",
|
| 32 |
+
token=HF_TOKEN,
|
| 33 |
+
trust_remote_code=True,
|
| 34 |
+
)
|
| 35 |
+
print(f"📊 Dataset chargé: {len(dataset)} exemples")
|
| 36 |
+
|
| 37 |
+
try:
|
| 38 |
+
self.df = dataset.to_pandas()
|
| 39 |
+
print("✅ Conversion to_pandas() réussie")
|
| 40 |
+
except Exception as pandas_error:
|
| 41 |
+
print(f"❌ Erreur to_pandas(): {pandas_error}")
|
| 42 |
+
print("🔄 Tentative de conversion manuelle...")
|
| 43 |
+
|
| 44 |
+
try:
|
| 45 |
+
data_list = []
|
| 46 |
+
max_examples = min(len(dataset), 1000) # Limiter pour éviter les problèmes de mémoire
|
| 47 |
+
|
| 48 |
+
for i, item in enumerate(dataset):
|
| 49 |
+
if i >= max_examples:
|
| 50 |
+
break
|
| 51 |
+
data_list.append(item)
|
| 52 |
+
if i < 5:
|
| 53 |
+
print(f"📋 Exemple {i}: {list(item.keys())}")
|
| 54 |
+
|
| 55 |
+
self.df = pd.DataFrame(data_list)
|
| 56 |
+
print(f"✅ Conversion manuelle réussie: {len(self.df)} lignes")
|
| 57 |
+
except Exception as manual_error:
|
| 58 |
+
print(f"❌ Erreur lors de la conversion manuelle: {manual_error}")
|
| 59 |
+
self.df = None
|
| 60 |
+
|
| 61 |
+
except Exception as e:
|
| 62 |
+
print(f"❌ Erreur lors du chargement depuis Hugging Face: {str(e)}")
|
| 63 |
+
print(f"❌ Type d'erreur: {type(e).__name__}")
|
| 64 |
+
|
| 65 |
+
# 2) Fallback: récupérer directement les fichiers du repo
|
| 66 |
+
try:
|
| 67 |
+
fallback_msg = self._fallback_load_from_repo_files()
|
| 68 |
+
if self.df is None and fallback_msg:
|
| 69 |
+
return f"❌ Erreur lors du chargement du dataset : {str(e)} | Fallback: {fallback_msg}"
|
| 70 |
+
except Exception as fallback_error:
|
| 71 |
+
print(f"❌ Erreur dans le fallback: {fallback_error}")
|
| 72 |
+
# Continue vers le chargement local
|
| 73 |
+
|
| 74 |
+
# Si on n'a toujours pas de dataframe, arrêter
|
| 75 |
if self.df is None:
|
| 76 |
+
print("⚠️ Aucune méthode de chargement n'a fonctionné")
|
| 77 |
+
return MESSAGES["no_data"]
|
| 78 |
|
| 79 |
+
print(f"📊 Données chargées: {len(self.df)} lignes")
|
| 80 |
+
print(f"📊 Colonnes disponibles: {list(self.df.columns)}")
|
|
|
|
| 81 |
|
| 82 |
+
# Nettoyage et validation
|
| 83 |
+
return self._clean_and_validate_data()
|
| 84 |
+
|
| 85 |
+
except Exception as e:
|
| 86 |
+
print(f"❌ Erreur critique dans load_data: {e}")
|
| 87 |
+
import traceback
|
| 88 |
+
traceback.print_exc()
|
| 89 |
+
return f"❌ Erreur critique lors du chargement: {str(e)}"
|
| 90 |
|
| 91 |
def _clean_and_validate_data(self):
|
| 92 |
"""Nettoie et valide les données chargées"""
|
interface.py
CHANGED
|
@@ -22,43 +22,126 @@ class AgricultureInterface:
|
|
| 22 |
self._initialize_data()
|
| 23 |
|
| 24 |
def _initialize_data(self):
|
| 25 |
-
"""Initialise les données au démarrage"""
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
self.
|
| 29 |
-
self.
|
| 30 |
-
|
| 31 |
-
self.
|
| 32 |
-
self.
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
def refresh_data(self):
|
| 36 |
-
"""Rafraîchit toutes les données"""
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
self.data_loader.
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
return (
|
| 56 |
-
"❌
|
| 57 |
empty_fig,
|
| 58 |
empty_fig,
|
| 59 |
empty_fig,
|
| 60 |
-
"❌
|
| 61 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
def create_interface(self):
|
| 64 |
"""Crée l'interface Gradio"""
|
|
@@ -103,37 +186,46 @@ class AgricultureInterface:
|
|
| 103 |
return demo
|
| 104 |
|
| 105 |
def _create_overview_tab(self):
|
| 106 |
-
"""Crée l'onglet de vue d'ensemble"""
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
def _create_risk_analysis_tab(self):
|
| 116 |
-
"""Crée l'onglet d'analyse des risques"""
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
def _create_recommendations_tab(self):
|
| 132 |
-
"""Crée l'onglet des recommandations"""
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
|
|
|
| 137 |
|
| 138 |
### Parcelles à Très Faible Risque (Vertes)
|
| 139 |
- ✅ **Idéales pour pois et haricot**
|
|
@@ -155,6 +247,9 @@ class AgricultureInterface:
|
|
| 155 |
- **Techniques mécaniques**: Hersage, binage
|
| 156 |
- **Biostimulants**: Renforcement naturel des cultures
|
| 157 |
""")
|
|
|
|
|
|
|
|
|
|
| 158 |
|
| 159 |
def _create_about_tab(self):
|
| 160 |
"""Crée l'onglet à propos"""
|
|
|
|
| 22 |
self._initialize_data()
|
| 23 |
|
| 24 |
def _initialize_data(self):
|
| 25 |
+
"""Initialise les données au démarrage avec gestion d'erreur"""
|
| 26 |
+
try:
|
| 27 |
+
print("🔄 Initialisation des données...")
|
| 28 |
+
self.data_loader.load_data()
|
| 29 |
+
if self.data_loader.has_data():
|
| 30 |
+
self.analyzer.set_data(self.data_loader.get_data())
|
| 31 |
+
self.analyzer.analyze_data()
|
| 32 |
+
self.visualizer.set_data(
|
| 33 |
+
self.data_loader.get_data(),
|
| 34 |
+
self.analyzer.get_risk_analysis()
|
| 35 |
+
)
|
| 36 |
+
print("✅ Initialisation réussie")
|
| 37 |
+
else:
|
| 38 |
+
print("⚠️ Aucune donnée disponible au démarrage")
|
| 39 |
+
except Exception as e:
|
| 40 |
+
print(f"❌ Erreur lors de l'initialisation: {e}")
|
| 41 |
+
# L'application peut continuer même si l'initialisation échoue
|
| 42 |
|
| 43 |
def refresh_data(self):
|
| 44 |
+
"""Rafraîchit toutes les données avec gestion d'erreur robuste"""
|
| 45 |
+
try:
|
| 46 |
+
print("🔄 Rafraîchissement des données...")
|
| 47 |
+
|
| 48 |
+
# Chargement des données
|
| 49 |
+
try:
|
| 50 |
+
self.data_loader.load_data()
|
| 51 |
+
except Exception as e:
|
| 52 |
+
print(f"❌ Erreur lors du chargement: {e}")
|
| 53 |
+
return self._get_error_outputs("Erreur lors du chargement des données")
|
| 54 |
+
|
| 55 |
+
if self.data_loader.has_data():
|
| 56 |
+
try:
|
| 57 |
+
# Analyse des données
|
| 58 |
+
self.analyzer.set_data(self.data_loader.get_data())
|
| 59 |
+
self.analyzer.analyze_data()
|
| 60 |
+
self.visualizer.set_data(
|
| 61 |
+
self.data_loader.get_data(),
|
| 62 |
+
self.analyzer.get_risk_analysis()
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
# Génération des outputs avec gestion d'erreur individuelle
|
| 66 |
+
return (
|
| 67 |
+
self._safe_get_summary_stats(),
|
| 68 |
+
self._safe_create_culture_analysis(),
|
| 69 |
+
self._safe_create_risk_distribution(),
|
| 70 |
+
self._safe_create_risk_visualization(),
|
| 71 |
+
self._safe_get_recommendations()
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
except Exception as e:
|
| 75 |
+
print(f"❌ Erreur lors de l'analyse: {e}")
|
| 76 |
+
return self._get_error_outputs(f"Erreur lors de l'analyse: {str(e)[:100]}...")
|
| 77 |
+
else:
|
| 78 |
+
print("⚠️ Aucune donnée disponible après chargement")
|
| 79 |
+
return self._get_error_outputs("Aucune donnée disponible")
|
| 80 |
+
|
| 81 |
+
except Exception as e:
|
| 82 |
+
print(f"❌ Erreur critique dans refresh_data: {e}")
|
| 83 |
+
return self._get_error_outputs("Erreur critique lors du rafraîchissement")
|
| 84 |
+
|
| 85 |
+
def _get_error_outputs(self, error_message):
|
| 86 |
+
"""Retourne des outputs d'erreur standardisés"""
|
| 87 |
+
try:
|
| 88 |
+
empty_fig = self.visualizer._create_error_plot("❌ Erreur", error_message)
|
| 89 |
return (
|
| 90 |
+
f"❌ {error_message}",
|
| 91 |
empty_fig,
|
| 92 |
empty_fig,
|
| 93 |
empty_fig,
|
| 94 |
+
f"❌ {error_message}"
|
| 95 |
)
|
| 96 |
+
except:
|
| 97 |
+
# Fallback ultime si même la création d'erreur échoue
|
| 98 |
+
return (
|
| 99 |
+
"❌ Erreur critique",
|
| 100 |
+
None,
|
| 101 |
+
None,
|
| 102 |
+
None,
|
| 103 |
+
"❌ Erreur critique"
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
def _safe_get_summary_stats(self):
|
| 107 |
+
"""Récupère les statistiques avec gestion d'erreur"""
|
| 108 |
+
try:
|
| 109 |
+
return self.analyzer.get_summary_stats()
|
| 110 |
+
except Exception as e:
|
| 111 |
+
print(f"❌ Erreur dans get_summary_stats: {e}")
|
| 112 |
+
return "❌ Erreur lors de la génération des statistiques"
|
| 113 |
+
|
| 114 |
+
def _safe_create_culture_analysis(self):
|
| 115 |
+
"""Crée l'analyse des cultures avec gestion d'erreur"""
|
| 116 |
+
try:
|
| 117 |
+
return self.visualizer.create_culture_analysis()
|
| 118 |
+
except Exception as e:
|
| 119 |
+
print(f"❌ Erreur dans create_culture_analysis: {e}")
|
| 120 |
+
return self.visualizer._create_error_plot("❌ Erreur", "Impossible de créer l'analyse des cultures")
|
| 121 |
+
|
| 122 |
+
def _safe_create_risk_distribution(self):
|
| 123 |
+
"""Crée la distribution des risques avec gestion d'erreur"""
|
| 124 |
+
try:
|
| 125 |
+
return self.visualizer.create_risk_distribution()
|
| 126 |
+
except Exception as e:
|
| 127 |
+
print(f"❌ Erreur dans create_risk_distribution: {e}")
|
| 128 |
+
return self.visualizer._create_error_plot("❌ Erreur", "Impossible de créer la distribution des risques")
|
| 129 |
+
|
| 130 |
+
def _safe_create_risk_visualization(self):
|
| 131 |
+
"""Crée la visualisation des risques avec gestion d'erreur"""
|
| 132 |
+
try:
|
| 133 |
+
return self.visualizer.create_risk_visualization()
|
| 134 |
+
except Exception as e:
|
| 135 |
+
print(f"❌ Erreur dans create_risk_visualization: {e}")
|
| 136 |
+
return self.visualizer._create_error_plot("❌ Erreur", "Impossible de créer la visualisation des risques")
|
| 137 |
+
|
| 138 |
+
def _safe_get_recommendations(self):
|
| 139 |
+
"""Récupère les recommandations avec gestion d'erreur"""
|
| 140 |
+
try:
|
| 141 |
+
return self.analyzer.get_low_risk_recommendations()
|
| 142 |
+
except Exception as e:
|
| 143 |
+
print(f"❌ Erreur dans get_low_risk_recommendations: {e}")
|
| 144 |
+
return "❌ Erreur lors de la génération des recommandations"
|
| 145 |
|
| 146 |
def create_interface(self):
|
| 147 |
"""Crée l'interface Gradio"""
|
|
|
|
| 186 |
return demo
|
| 187 |
|
| 188 |
def _create_overview_tab(self):
|
| 189 |
+
"""Crée l'onglet de vue d'ensemble avec gestion d'erreur"""
|
| 190 |
+
try:
|
| 191 |
+
gr.Markdown("## Statistiques générales des données agricoles")
|
| 192 |
+
|
| 193 |
+
self.stats_output = gr.Markdown(self._safe_get_summary_stats())
|
| 194 |
+
|
| 195 |
+
with gr.Row():
|
| 196 |
+
self.culture_plot = gr.Plot(self._safe_create_culture_analysis())
|
| 197 |
+
self.risk_dist_plot = gr.Plot(self._safe_create_risk_distribution())
|
| 198 |
+
except Exception as e:
|
| 199 |
+
print(f"❌ Erreur lors de la création de l'onglet vue d'ensemble: {e}")
|
| 200 |
+
gr.Markdown("❌ Erreur lors de la création de l'interface")
|
| 201 |
|
| 202 |
def _create_risk_analysis_tab(self):
|
| 203 |
+
"""Crée l'onglet d'analyse des risques avec gestion d'erreur"""
|
| 204 |
+
try:
|
| 205 |
+
gr.Markdown("## Cartographie des risques adventices par parcelle")
|
| 206 |
+
|
| 207 |
+
self.risk_plot = gr.Plot(self._safe_create_risk_visualization())
|
| 208 |
+
|
| 209 |
+
gr.Markdown("""
|
| 210 |
+
**Interprétation du graphique**:
|
| 211 |
+
- **Axe X**: Surface de la parcelle (hectares)
|
| 212 |
+
- **Axe Y**: IFT Herbicide approximatif
|
| 213 |
+
- **Couleur**: Niveau de risque adventice
|
| 214 |
+
- **Taille**: Nombre d'herbicides utilisés
|
| 215 |
+
|
| 216 |
+
Les parcelles vertes (risque faible) sont idéales pour les cultures sensibles.
|
| 217 |
+
""")
|
| 218 |
+
except Exception as e:
|
| 219 |
+
print(f"❌ Erreur lors de la création de l'onglet analyse des risques: {e}")
|
| 220 |
+
gr.Markdown("❌ Erreur lors de la création de l'interface d'analyse des risques")
|
| 221 |
|
| 222 |
def _create_recommendations_tab(self):
|
| 223 |
+
"""Crée l'onglet des recommandations avec gestion d'erreur"""
|
| 224 |
+
try:
|
| 225 |
+
self.reco_output = gr.Markdown(self._safe_get_recommendations())
|
| 226 |
+
|
| 227 |
+
gr.Markdown("""
|
| 228 |
+
## 💡 Conseils pour la gestion des adventices
|
| 229 |
|
| 230 |
### Parcelles à Très Faible Risque (Vertes)
|
| 231 |
- ✅ **Idéales pour pois et haricot**
|
|
|
|
| 247 |
- **Techniques mécaniques**: Hersage, binage
|
| 248 |
- **Biostimulants**: Renforcement naturel des cultures
|
| 249 |
""")
|
| 250 |
+
except Exception as e:
|
| 251 |
+
print(f"❌ Erreur lors de la création de l'onglet recommandations: {e}")
|
| 252 |
+
gr.Markdown("❌ Erreur lors de la création de l'interface de recommandations")
|
| 253 |
|
| 254 |
def _create_about_tab(self):
|
| 255 |
"""Crée l'onglet à propos"""
|
main.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
"""
|
| 2 |
Point d'entrée principal de l'application d'analyse des adventices agricoles
|
|
|
|
| 3 |
"""
|
| 4 |
import warnings
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import seaborn as sns
|
| 7 |
-
|
|
|
|
| 8 |
|
| 9 |
# Suppression des warnings
|
| 10 |
warnings.filterwarnings('ignore')
|
|
@@ -14,13 +16,77 @@ plt.style.use('default')
|
|
| 14 |
sns.set_palette("husl")
|
| 15 |
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
def main():
|
| 18 |
-
"""Fonction principale
|
| 19 |
-
print("🌾
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
if __name__ == "__main__":
|
|
|
|
| 1 |
"""
|
| 2 |
Point d'entrée principal de l'application d'analyse des adventices agricoles
|
| 3 |
+
Avec détection automatique des problèmes et fallback
|
| 4 |
"""
|
| 5 |
import warnings
|
| 6 |
import matplotlib.pyplot as plt
|
| 7 |
import seaborn as sns
|
| 8 |
+
import sys
|
| 9 |
+
import os
|
| 10 |
|
| 11 |
# Suppression des warnings
|
| 12 |
warnings.filterwarnings('ignore')
|
|
|
|
| 16 |
sns.set_palette("husl")
|
| 17 |
|
| 18 |
|
| 19 |
+
def try_gradio_interface():
|
| 20 |
+
"""Essaie de lancer l'interface Gradio"""
|
| 21 |
+
try:
|
| 22 |
+
print("🔄 Tentative de lancement de l'interface Gradio...")
|
| 23 |
+
from interface import AgricultureInterface
|
| 24 |
+
|
| 25 |
+
app = AgricultureInterface()
|
| 26 |
+
app.launch()
|
| 27 |
+
return True
|
| 28 |
+
|
| 29 |
+
except ImportError as e:
|
| 30 |
+
print(f"❌ Erreur d'import: {e}")
|
| 31 |
+
if "pyaudioop" in str(e) or "audioop" in str(e):
|
| 32 |
+
print("🔍 Problème détecté: Dépendance audio manquante pour Gradio")
|
| 33 |
+
return False
|
| 34 |
+
|
| 35 |
+
except Exception as e:
|
| 36 |
+
print(f"❌ Erreur lors du lancement de Gradio: {e}")
|
| 37 |
+
return False
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def run_fallback():
|
| 41 |
+
"""Lance le mode fallback sans Gradio"""
|
| 42 |
+
try:
|
| 43 |
+
print("🔄 Basculement vers le mode fallback...")
|
| 44 |
+
from main_fallback import run_analysis_without_ui
|
| 45 |
+
|
| 46 |
+
return run_analysis_without_ui()
|
| 47 |
+
|
| 48 |
+
except Exception as e:
|
| 49 |
+
print(f"❌ Erreur dans le mode fallback: {e}")
|
| 50 |
+
return False
|
| 51 |
+
|
| 52 |
+
|
| 53 |
def main():
|
| 54 |
+
"""Fonction principale avec gestion d'erreur et fallback automatique"""
|
| 55 |
+
print("🌾 Application d'Analyse des Adventices Agricoles")
|
| 56 |
+
print("=" * 60)
|
| 57 |
+
|
| 58 |
+
# Tentative 1: Interface Gradio complète
|
| 59 |
+
print("\n🎯 Tentative 1: Interface web avec Gradio")
|
| 60 |
+
|
| 61 |
+
try:
|
| 62 |
+
if try_gradio_interface():
|
| 63 |
+
return # Succès, on s'arrête ici
|
| 64 |
+
except KeyboardInterrupt:
|
| 65 |
+
print("\n⏹️ Arrêt demandé par l'utilisateur")
|
| 66 |
+
return
|
| 67 |
+
except Exception as e:
|
| 68 |
+
print(f"❌ Échec critique de l'interface Gradio: {e}")
|
| 69 |
+
|
| 70 |
+
# Tentative 2: Mode fallback console
|
| 71 |
+
print("\n🎯 Tentative 2: Mode console (fallback)")
|
| 72 |
|
| 73 |
+
try:
|
| 74 |
+
if run_fallback():
|
| 75 |
+
print("\n✅ Mode fallback exécuté avec succès")
|
| 76 |
+
print("\n💡 Pour utiliser l'interface web, résolvez les problèmes de dépendances:")
|
| 77 |
+
print(" - Installez les dépendances audio manquantes")
|
| 78 |
+
print(" - Ou utilisez une version compatible de Gradio")
|
| 79 |
+
else:
|
| 80 |
+
print("\n❌ Échec du mode fallback")
|
| 81 |
+
exit(1)
|
| 82 |
+
|
| 83 |
+
except KeyboardInterrupt:
|
| 84 |
+
print("\n⏹️ Arrêt demandé par l'utilisateur")
|
| 85 |
+
return
|
| 86 |
+
except Exception as e:
|
| 87 |
+
print(f"\n❌ Erreur critique dans le mode fallback: {e}")
|
| 88 |
+
print("🆘 Aucun mode disponible - vérifiez vos dépendances")
|
| 89 |
+
exit(1)
|
| 90 |
|
| 91 |
|
| 92 |
if __name__ == "__main__":
|
main_fallback.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Version alternative de main.py qui fonctionne sans Gradio
|
| 3 |
+
Pour les cas où les dépendances Gradio posent problème
|
| 4 |
+
"""
|
| 5 |
+
import warnings
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
from data_loader import DataLoader
|
| 9 |
+
from analyzer import AgricultureAnalyzer
|
| 10 |
+
from visualizations import AgricultureVisualizer
|
| 11 |
+
|
| 12 |
+
# Suppression des warnings
|
| 13 |
+
warnings.filterwarnings('ignore')
|
| 14 |
+
|
| 15 |
+
# Configuration des graphiques
|
| 16 |
+
plt.style.use('default')
|
| 17 |
+
sns.set_palette("husl")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def run_analysis_without_ui():
|
| 21 |
+
"""Exécute l'analyse sans interface utilisateur"""
|
| 22 |
+
print("🌾 Analyse des Adventices Agricoles - Mode Console")
|
| 23 |
+
print("=" * 60)
|
| 24 |
+
|
| 25 |
+
try:
|
| 26 |
+
# Initialisation des composants
|
| 27 |
+
print("🔄 Initialisation des composants...")
|
| 28 |
+
data_loader = DataLoader()
|
| 29 |
+
analyzer = AgricultureAnalyzer()
|
| 30 |
+
visualizer = AgricultureVisualizer()
|
| 31 |
+
|
| 32 |
+
# Chargement des données
|
| 33 |
+
print("\n📊 Chargement des données...")
|
| 34 |
+
result = data_loader.load_data()
|
| 35 |
+
if isinstance(result, str) and "❌" in result:
|
| 36 |
+
print(f"Erreur: {result}")
|
| 37 |
+
return False
|
| 38 |
+
|
| 39 |
+
if not data_loader.has_data():
|
| 40 |
+
print("❌ Aucune donnée disponible")
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
data = data_loader.get_data()
|
| 44 |
+
print(f"✅ {len(data)} enregistrements chargés")
|
| 45 |
+
|
| 46 |
+
# Analyse des données
|
| 47 |
+
print("\n🔬 Analyse des données...")
|
| 48 |
+
analyzer.set_data(data)
|
| 49 |
+
general_stats, herbicide_stats = analyzer.analyze_data()
|
| 50 |
+
|
| 51 |
+
if general_stats:
|
| 52 |
+
print("✅ Analyse générale terminée")
|
| 53 |
+
print(f" - Parcelles: {general_stats['total_parcelles']}")
|
| 54 |
+
print(f" - Interventions: {general_stats['total_interventions']}")
|
| 55 |
+
print(f" - Surface totale: {general_stats['surface_totale']:.2f} ha")
|
| 56 |
+
|
| 57 |
+
if herbicide_stats:
|
| 58 |
+
print("✅ Analyse herbicides terminée")
|
| 59 |
+
print(f" - Interventions herbicides: {herbicide_stats['nb_interventions_herbicides']}")
|
| 60 |
+
print(f" - Pourcentage: {herbicide_stats['pourcentage_herbicides']:.1f}%")
|
| 61 |
+
|
| 62 |
+
# Génération des statistiques
|
| 63 |
+
print("\n📋 Génération des statistiques...")
|
| 64 |
+
stats = analyzer.get_summary_stats()
|
| 65 |
+
print("✅ Statistiques générées")
|
| 66 |
+
|
| 67 |
+
# Génération des recommandations
|
| 68 |
+
print("\n🎯 Génération des recommandations...")
|
| 69 |
+
recommendations = analyzer.get_low_risk_recommendations()
|
| 70 |
+
print("✅ Recommandations générées")
|
| 71 |
+
|
| 72 |
+
# Sauvegarde des résultats
|
| 73 |
+
print("\n💾 Sauvegarde des résultats...")
|
| 74 |
+
try:
|
| 75 |
+
# Sauvegarde des statistiques
|
| 76 |
+
with open('results/stats_console.md', 'w', encoding='utf-8') as f:
|
| 77 |
+
f.write("# Analyse des Adventices Agricoles\n\n")
|
| 78 |
+
f.write(stats)
|
| 79 |
+
f.write("\n\n")
|
| 80 |
+
f.write(recommendations)
|
| 81 |
+
|
| 82 |
+
# Sauvegarde de l'analyse des risques si disponible
|
| 83 |
+
risk_analysis = analyzer.get_risk_analysis()
|
| 84 |
+
if risk_analysis is not None and len(risk_analysis) > 0:
|
| 85 |
+
risk_analysis.to_csv('results/risk_analysis_console.csv')
|
| 86 |
+
print("✅ Analyse des risques sauvegardée (CSV)")
|
| 87 |
+
|
| 88 |
+
print("✅ Résultats sauvegardés dans results/")
|
| 89 |
+
|
| 90 |
+
except Exception as e:
|
| 91 |
+
print(f"⚠️ Erreur lors de la sauvegarde: {e}")
|
| 92 |
+
|
| 93 |
+
# Tentative de génération des graphiques
|
| 94 |
+
print("\n📈 Tentative de génération des graphiques...")
|
| 95 |
+
try:
|
| 96 |
+
visualizer.set_data(data, analyzer.get_risk_analysis())
|
| 97 |
+
|
| 98 |
+
# Les graphiques seront créés mais ne pourront pas être affichés en mode console
|
| 99 |
+
print("⚠️ Les graphiques sont générés mais ne peuvent pas être affichés en mode console")
|
| 100 |
+
print("💡 Utilisez main.py avec Gradio pour voir les visualisations interactives")
|
| 101 |
+
|
| 102 |
+
except Exception as e:
|
| 103 |
+
print(f"⚠️ Impossible de générer les graphiques: {e}")
|
| 104 |
+
|
| 105 |
+
print("\n🎉 Analyse terminée avec succès!")
|
| 106 |
+
print("\n📁 Fichiers générés:")
|
| 107 |
+
print(" - results/stats_console.md : Statistiques et recommandations")
|
| 108 |
+
print(" - results/risk_analysis_console.csv : Analyse détaillée des risques")
|
| 109 |
+
|
| 110 |
+
return True
|
| 111 |
+
|
| 112 |
+
except Exception as e:
|
| 113 |
+
print(f"❌ Erreur critique: {e}")
|
| 114 |
+
import traceback
|
| 115 |
+
traceback.print_exc()
|
| 116 |
+
return False
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def main():
|
| 120 |
+
"""Fonction principale en mode fallback"""
|
| 121 |
+
print("🔧 Mode Fallback - Analyse sans interface Gradio")
|
| 122 |
+
|
| 123 |
+
try:
|
| 124 |
+
# Tentative d'import de Gradio pour vérifier la disponibilité
|
| 125 |
+
try:
|
| 126 |
+
import gradio as gr
|
| 127 |
+
print("✅ Gradio disponible - vous pouvez utiliser main.py")
|
| 128 |
+
print("⚠️ Utilisation du mode fallback forcé")
|
| 129 |
+
except ImportError as e:
|
| 130 |
+
print(f"⚠️ Gradio non disponible: {e}")
|
| 131 |
+
print("🔄 Utilisation du mode console")
|
| 132 |
+
except Exception as e:
|
| 133 |
+
print(f"⚠️ Problème avec Gradio: {e}")
|
| 134 |
+
print("🔄 Utilisation du mode console")
|
| 135 |
+
|
| 136 |
+
# Exécution de l'analyse
|
| 137 |
+
success = run_analysis_without_ui()
|
| 138 |
+
|
| 139 |
+
if success:
|
| 140 |
+
print("\n✅ Mode fallback exécuté avec succès")
|
| 141 |
+
else:
|
| 142 |
+
print("\n❌ Échec du mode fallback")
|
| 143 |
+
exit(1)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
if __name__ == "__main__":
|
| 147 |
+
main()
|
visualizations.py
CHANGED
|
@@ -19,162 +19,334 @@ class AgricultureVisualizer:
|
|
| 19 |
if risk_analysis is not None:
|
| 20 |
self.risk_analysis = risk_analysis
|
| 21 |
|
| 22 |
-
def
|
| 23 |
-
"""Crée
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
fig = px.scatter(title="❌ Aucune donnée d'analyse des risques disponible")
|
| 27 |
fig.add_annotation(
|
| 28 |
-
text=
|
| 29 |
-
xref="paper", yref="paper",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
)
|
| 31 |
return fig
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
available_hover_cols = []
|
| 37 |
-
for col in ['nomparc', 'libelleusag']:
|
| 38 |
-
if col in risk_df.columns:
|
| 39 |
-
available_hover_cols.append(col)
|
| 40 |
-
|
| 41 |
-
fig = px.scatter(
|
| 42 |
-
risk_df,
|
| 43 |
-
x='surfparc',
|
| 44 |
-
y='IFT_herbicide_approx',
|
| 45 |
-
color='Risque_adventice',
|
| 46 |
-
size='Nb_herbicides',
|
| 47 |
-
hover_data=available_hover_cols if available_hover_cols else None,
|
| 48 |
-
color_discrete_map=RISK_COLORS,
|
| 49 |
-
title="🎯 Analyse du Risque Adventice par Parcelle",
|
| 50 |
-
labels={
|
| 51 |
-
'surfparc': 'Surface de la parcelle (ha)',
|
| 52 |
-
'IFT_herbicide_approx': 'IFT Herbicide (approximatif)',
|
| 53 |
-
'Risque_adventice': 'Niveau de risque'
|
| 54 |
-
}
|
| 55 |
-
)
|
| 56 |
-
|
| 57 |
-
fig.update_layout(
|
| 58 |
-
width=PLOT_CONFIG["width"],
|
| 59 |
-
height=PLOT_CONFIG["height"],
|
| 60 |
-
title_font_size=PLOT_CONFIG["title_font_size"]
|
| 61 |
-
)
|
| 62 |
-
return fig
|
| 63 |
|
| 64 |
-
def
|
| 65 |
-
"""
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
)
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
)
|
| 81 |
-
return fig
|
| 82 |
-
|
| 83 |
-
culture_counts = self.df['libelleusag'].value_counts()
|
| 84 |
-
|
| 85 |
-
fig = px.pie(
|
| 86 |
-
values=culture_counts.values,
|
| 87 |
-
names=culture_counts.index,
|
| 88 |
-
title="🌱 Répartition des Cultures"
|
| 89 |
-
)
|
| 90 |
-
|
| 91 |
-
fig.update_layout(width=700, height=500)
|
| 92 |
-
return fig
|
| 93 |
|
| 94 |
def create_risk_distribution(self):
|
| 95 |
-
"""Distribution des niveaux de risque"""
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
)
|
| 103 |
-
return fig
|
| 104 |
-
|
| 105 |
-
risk_counts = self.risk_analysis['Risque_adventice'].value_counts()
|
| 106 |
-
|
| 107 |
-
fig = px.bar(
|
| 108 |
-
x=risk_counts.index,
|
| 109 |
-
y=risk_counts.values,
|
| 110 |
-
color=risk_counts.index,
|
| 111 |
-
color_discrete_map=RISK_COLORS,
|
| 112 |
-
title="📊 Distribution des Niveaux de Risque Adventice",
|
| 113 |
-
labels={'x': 'Niveau de risque', 'y': 'Nombre de parcelles'}
|
| 114 |
-
)
|
| 115 |
-
|
| 116 |
-
fig.update_layout(width=700, height=500, showlegend=False)
|
| 117 |
-
return fig
|
| 118 |
|
| 119 |
def create_herbicide_timeline(self):
|
| 120 |
-
"""Crée un graphique de l'évolution temporelle des herbicides"""
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
)
|
| 135 |
-
return fig
|
| 136 |
-
|
| 137 |
-
# Filtrer les herbicides et grouper par année
|
| 138 |
-
herbicides_df = self.df[self.df['familleprod'] == 'Herbicides']
|
| 139 |
-
if len(herbicides_df) == 0:
|
| 140 |
-
fig = px.line(title="❌ Aucune donnée d'herbicide disponible")
|
| 141 |
-
return fig
|
| 142 |
-
|
| 143 |
-
yearly_herbicides = herbicides_df.groupby('millesime').agg({
|
| 144 |
-
'numparcell': 'nunique',
|
| 145 |
-
'quantitetot': 'sum'
|
| 146 |
-
}).reset_index()
|
| 147 |
-
|
| 148 |
-
fig = px.line(
|
| 149 |
-
yearly_herbicides,
|
| 150 |
-
x='millesime',
|
| 151 |
-
y='quantitetot',
|
| 152 |
-
title="📈 Évolution de l'Usage des Herbicides par Année",
|
| 153 |
-
labels={
|
| 154 |
-
'millesime': 'Année',
|
| 155 |
-
'quantitetot': 'Quantité totale d\'herbicides'
|
| 156 |
-
}
|
| 157 |
-
)
|
| 158 |
-
|
| 159 |
-
fig.update_layout(width=700, height=400)
|
| 160 |
-
return fig
|
| 161 |
|
| 162 |
def create_surface_analysis(self):
|
| 163 |
-
"""Analyse de la distribution des surfaces"""
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
if risk_analysis is not None:
|
| 20 |
self.risk_analysis = risk_analysis
|
| 21 |
|
| 22 |
+
def _create_error_plot(self, title, message):
|
| 23 |
+
"""Crée un graphique d'erreur standardisé"""
|
| 24 |
+
try:
|
| 25 |
+
fig = px.scatter(title=title)
|
|
|
|
| 26 |
fig.add_annotation(
|
| 27 |
+
text=message,
|
| 28 |
+
xref="paper", yref="paper",
|
| 29 |
+
x=0.5, y=0.5,
|
| 30 |
+
showarrow=False,
|
| 31 |
+
font=dict(size=14, color="red")
|
| 32 |
+
)
|
| 33 |
+
fig.update_layout(
|
| 34 |
+
width=PLOT_CONFIG.get("width", 700),
|
| 35 |
+
height=PLOT_CONFIG.get("height", 400)
|
| 36 |
)
|
| 37 |
return fig
|
| 38 |
+
except Exception as e:
|
| 39 |
+
print(f"❌ Erreur lors de la création du graphique d'erreur: {e}")
|
| 40 |
+
# Retourner un graphique minimal en cas d'échec total
|
| 41 |
+
return go.Figure().add_annotation(text="Erreur critique", x=0.5, y=0.5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
+
def create_risk_visualization(self):
|
| 44 |
+
"""Crée la visualisation des risques avec gestion d'erreur robuste"""
|
| 45 |
+
try:
|
| 46 |
+
if self.risk_analysis is None or len(self.risk_analysis) == 0:
|
| 47 |
+
return self._create_error_plot(
|
| 48 |
+
"❌ Aucune donnée d'analyse des risques disponible",
|
| 49 |
+
"Veuillez charger et analyser les données d'abord"
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
risk_df = self.risk_analysis.reset_index()
|
| 54 |
+
|
| 55 |
+
# Vérifier les colonnes requises
|
| 56 |
+
required_cols = ['surfparc', 'IFT_herbicide_approx', 'Risque_adventice', 'Nb_herbicides']
|
| 57 |
+
missing_cols = [col for col in required_cols if col not in risk_df.columns]
|
| 58 |
+
|
| 59 |
+
if missing_cols:
|
| 60 |
+
return self._create_error_plot(
|
| 61 |
+
f"❌ Colonnes manquantes: {missing_cols}",
|
| 62 |
+
"Les données ne contiennent pas toutes les colonnes nécessaires"
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
# Vérifier quelles colonnes sont disponibles pour hover_data
|
| 66 |
+
available_hover_cols = []
|
| 67 |
+
for col in ['nomparc', 'libelleusag']:
|
| 68 |
+
if col in risk_df.columns:
|
| 69 |
+
available_hover_cols.append(col)
|
| 70 |
+
|
| 71 |
+
# Nettoyer les données pour éviter les erreurs de plotting
|
| 72 |
+
risk_df = risk_df.dropna(subset=required_cols)
|
| 73 |
+
|
| 74 |
+
if len(risk_df) == 0:
|
| 75 |
+
return self._create_error_plot(
|
| 76 |
+
"❌ Aucune donnée valide après nettoyage",
|
| 77 |
+
"Toutes les données contiennent des valeurs manquantes"
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
fig = px.scatter(
|
| 81 |
+
risk_df,
|
| 82 |
+
x='surfparc',
|
| 83 |
+
y='IFT_herbicide_approx',
|
| 84 |
+
color='Risque_adventice',
|
| 85 |
+
size='Nb_herbicides',
|
| 86 |
+
hover_data=available_hover_cols if available_hover_cols else None,
|
| 87 |
+
color_discrete_map=RISK_COLORS,
|
| 88 |
+
title="🎯 Analyse du Risque Adventice par Parcelle",
|
| 89 |
+
labels={
|
| 90 |
+
'surfparc': 'Surface de la parcelle (ha)',
|
| 91 |
+
'IFT_herbicide_approx': 'IFT Herbicide (approximatif)',
|
| 92 |
+
'Risque_adventice': 'Niveau de risque'
|
| 93 |
+
}
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
fig.update_layout(
|
| 97 |
+
width=PLOT_CONFIG["width"],
|
| 98 |
+
height=PLOT_CONFIG["height"],
|
| 99 |
+
title_font_size=PLOT_CONFIG["title_font_size"]
|
| 100 |
+
)
|
| 101 |
+
return fig
|
| 102 |
+
|
| 103 |
+
except Exception as e:
|
| 104 |
+
print(f"❌ Erreur lors de la création du graphique de risque: {e}")
|
| 105 |
+
return self._create_error_plot(
|
| 106 |
+
"❌ Erreur lors de la création du graphique",
|
| 107 |
+
f"Erreur technique: {str(e)[:100]}..."
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
except Exception as e:
|
| 111 |
+
print(f"❌ Erreur critique dans create_risk_visualization: {e}")
|
| 112 |
+
return self._create_error_plot(
|
| 113 |
+
"❌ Erreur critique",
|
| 114 |
+
"Impossible de créer la visualisation des risques"
|
| 115 |
)
|
| 116 |
+
|
| 117 |
+
def create_culture_analysis(self):
|
| 118 |
+
"""Analyse par type de culture avec gestion d'erreur robuste"""
|
| 119 |
+
try:
|
| 120 |
+
if self.df is None or len(self.df) == 0:
|
| 121 |
+
return self._create_error_plot(
|
| 122 |
+
"❌ Aucune donnée disponible",
|
| 123 |
+
"Veuillez charger les données d'abord"
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
if 'libelleusag' not in self.df.columns:
|
| 127 |
+
return self._create_error_plot(
|
| 128 |
+
"❌ Colonne 'libelleusag' non disponible",
|
| 129 |
+
"Les données de culture ne sont pas disponibles"
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
try:
|
| 133 |
+
# Nettoyer les données de culture
|
| 134 |
+
culture_data = self.df['libelleusag'].dropna()
|
| 135 |
+
|
| 136 |
+
if len(culture_data) == 0:
|
| 137 |
+
return self._create_error_plot(
|
| 138 |
+
"❌ Aucune donnée de culture valide",
|
| 139 |
+
"Toutes les valeurs de culture sont manquantes"
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
culture_counts = culture_data.value_counts()
|
| 143 |
+
|
| 144 |
+
if len(culture_counts) == 0:
|
| 145 |
+
return self._create_error_plot(
|
| 146 |
+
"❌ Aucune culture détectée",
|
| 147 |
+
"Aucune donnée de culture trouvée après nettoyage"
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
fig = px.pie(
|
| 151 |
+
values=culture_counts.values,
|
| 152 |
+
names=culture_counts.index,
|
| 153 |
+
title="🌱 Répartition des Cultures"
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
fig.update_layout(width=700, height=500)
|
| 157 |
+
return fig
|
| 158 |
+
|
| 159 |
+
except Exception as e:
|
| 160 |
+
print(f"❌ Erreur lors de la création du graphique de culture: {e}")
|
| 161 |
+
return self._create_error_plot(
|
| 162 |
+
"❌ Erreur lors de la création du graphique",
|
| 163 |
+
f"Erreur technique: {str(e)[:100]}..."
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
except Exception as e:
|
| 167 |
+
print(f"❌ Erreur critique dans create_culture_analysis: {e}")
|
| 168 |
+
return self._create_error_plot(
|
| 169 |
+
"❌ Erreur critique",
|
| 170 |
+
"Impossible de créer l'analyse des cultures"
|
| 171 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
def create_risk_distribution(self):
|
| 174 |
+
"""Distribution des niveaux de risque avec gestion d'erreur robuste"""
|
| 175 |
+
try:
|
| 176 |
+
if self.risk_analysis is None or len(self.risk_analysis) == 0:
|
| 177 |
+
return self._create_error_plot(
|
| 178 |
+
"❌ Aucune analyse des risques disponible",
|
| 179 |
+
"Veuillez charger et analyser les données d'abord"
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
if 'Risque_adventice' not in self.risk_analysis.columns:
|
| 183 |
+
return self._create_error_plot(
|
| 184 |
+
"❌ Colonne 'Risque_adventice' manquante",
|
| 185 |
+
"L'analyse des risques est incomplète"
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
try:
|
| 189 |
+
# Nettoyer les données de risque
|
| 190 |
+
risk_data = self.risk_analysis['Risque_adventice'].dropna()
|
| 191 |
+
|
| 192 |
+
if len(risk_data) == 0:
|
| 193 |
+
return self._create_error_plot(
|
| 194 |
+
"❌ Aucune donnée de risque valide",
|
| 195 |
+
"Toutes les valeurs de risque sont manquantes"
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
risk_counts = risk_data.value_counts()
|
| 199 |
+
|
| 200 |
+
if len(risk_counts) == 0:
|
| 201 |
+
return self._create_error_plot(
|
| 202 |
+
"❌ Aucun niveau de risque détecté",
|
| 203 |
+
"Aucune donnée de risque trouvée après nettoyage"
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
fig = px.bar(
|
| 207 |
+
x=risk_counts.index,
|
| 208 |
+
y=risk_counts.values,
|
| 209 |
+
color=risk_counts.index,
|
| 210 |
+
color_discrete_map=RISK_COLORS,
|
| 211 |
+
title="📊 Distribution des Niveaux de Risque Adventice",
|
| 212 |
+
labels={'x': 'Niveau de risque', 'y': 'Nombre de parcelles'}
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
fig.update_layout(width=700, height=500, showlegend=False)
|
| 216 |
+
return fig
|
| 217 |
+
|
| 218 |
+
except Exception as e:
|
| 219 |
+
print(f"❌ Erreur lors de la création du graphique de distribution: {e}")
|
| 220 |
+
return self._create_error_plot(
|
| 221 |
+
"❌ Erreur lors de la création du graphique",
|
| 222 |
+
f"Erreur technique: {str(e)[:100]}..."
|
| 223 |
+
)
|
| 224 |
+
|
| 225 |
+
except Exception as e:
|
| 226 |
+
print(f"❌ Erreur critique dans create_risk_distribution: {e}")
|
| 227 |
+
return self._create_error_plot(
|
| 228 |
+
"❌ Erreur critique",
|
| 229 |
+
"Impossible de créer la distribution des risques"
|
| 230 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
|
| 232 |
def create_herbicide_timeline(self):
|
| 233 |
+
"""Crée un graphique de l'évolution temporelle des herbicides avec gestion d'erreur"""
|
| 234 |
+
try:
|
| 235 |
+
if self.df is None or len(self.df) == 0:
|
| 236 |
+
return self._create_error_plot(
|
| 237 |
+
"❌ Aucune donnée disponible",
|
| 238 |
+
"Veuillez charger les données d'abord"
|
| 239 |
+
)
|
| 240 |
+
|
| 241 |
+
required_cols = ['millesime', 'familleprod']
|
| 242 |
+
missing_cols = [col for col in required_cols if col not in self.df.columns]
|
| 243 |
+
|
| 244 |
+
if missing_cols:
|
| 245 |
+
return self._create_error_plot(
|
| 246 |
+
f"❌ Colonnes manquantes: {missing_cols}",
|
| 247 |
+
"Les données temporelles ne sont pas disponibles"
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
try:
|
| 251 |
+
# Filtrer les herbicides et grouper par année
|
| 252 |
+
herbicides_df = self.df[self.df['familleprod'] == 'Herbicides']
|
| 253 |
+
if len(herbicides_df) == 0:
|
| 254 |
+
return self._create_error_plot(
|
| 255 |
+
"❌ Aucune donnée d'herbicide disponible",
|
| 256 |
+
"Aucune intervention herbicide trouvée dans les données"
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
agg_dict = {'numparcell': 'nunique'}
|
| 260 |
+
if 'quantitetot' in herbicides_df.columns:
|
| 261 |
+
agg_dict['quantitetot'] = 'sum'
|
| 262 |
+
|
| 263 |
+
yearly_herbicides = herbicides_df.groupby('millesime').agg(agg_dict).reset_index()
|
| 264 |
+
|
| 265 |
+
if len(yearly_herbicides) == 0:
|
| 266 |
+
return self._create_error_plot(
|
| 267 |
+
"❌ Aucune donnée temporelle valide",
|
| 268 |
+
"Impossible de grouper les données par année"
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
y_col = 'quantitetot' if 'quantitetot' in yearly_herbicides.columns else 'numparcell'
|
| 272 |
+
y_label = 'Quantité totale d\'herbicides' if y_col == 'quantitetot' else 'Nombre de parcelles traitées'
|
| 273 |
+
|
| 274 |
+
fig = px.line(
|
| 275 |
+
yearly_herbicides,
|
| 276 |
+
x='millesime',
|
| 277 |
+
y=y_col,
|
| 278 |
+
title="📈 Évolution de l'Usage des Herbicides par Année",
|
| 279 |
+
labels={
|
| 280 |
+
'millesime': 'Année',
|
| 281 |
+
y_col: y_label
|
| 282 |
+
}
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
fig.update_layout(width=700, height=400)
|
| 286 |
+
return fig
|
| 287 |
+
|
| 288 |
+
except Exception as e:
|
| 289 |
+
print(f"❌ Erreur lors de la création du graphique temporel: {e}")
|
| 290 |
+
return self._create_error_plot(
|
| 291 |
+
"❌ Erreur lors de la création du graphique",
|
| 292 |
+
f"Erreur technique: {str(e)[:100]}..."
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
except Exception as e:
|
| 296 |
+
print(f"❌ Erreur critique dans create_herbicide_timeline: {e}")
|
| 297 |
+
return self._create_error_plot(
|
| 298 |
+
"❌ Erreur critique",
|
| 299 |
+
"Impossible de créer l'évolution temporelle"
|
| 300 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
|
| 302 |
def create_surface_analysis(self):
|
| 303 |
+
"""Analyse de la distribution des surfaces avec gestion d'erreur"""
|
| 304 |
+
try:
|
| 305 |
+
if self.df is None or len(self.df) == 0:
|
| 306 |
+
return self._create_error_plot(
|
| 307 |
+
"❌ Aucune donnée disponible",
|
| 308 |
+
"Veuillez charger les données d'abord"
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
if 'surfparc' not in self.df.columns:
|
| 312 |
+
return self._create_error_plot(
|
| 313 |
+
"❌ Colonne 'surfparc' manquante",
|
| 314 |
+
"Les données de surface ne sont pas disponibles"
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
try:
|
| 318 |
+
# Nettoyer les données de surface
|
| 319 |
+
surface_data = self.df['surfparc'].dropna()
|
| 320 |
+
|
| 321 |
+
if len(surface_data) == 0:
|
| 322 |
+
return self._create_error_plot(
|
| 323 |
+
"❌ Aucune donnée de surface valide",
|
| 324 |
+
"Toutes les valeurs de surface sont manquantes"
|
| 325 |
+
)
|
| 326 |
+
|
| 327 |
+
fig = px.histogram(
|
| 328 |
+
x=surface_data,
|
| 329 |
+
nbins=20,
|
| 330 |
+
title="📏 Distribution des Surfaces de Parcelles",
|
| 331 |
+
labels={
|
| 332 |
+
'x': 'Surface (ha)',
|
| 333 |
+
'count': 'Nombre de parcelles'
|
| 334 |
+
}
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
fig.update_layout(width=700, height=400)
|
| 338 |
+
return fig
|
| 339 |
+
|
| 340 |
+
except Exception as e:
|
| 341 |
+
print(f"❌ Erreur lors de la création du graphique de surface: {e}")
|
| 342 |
+
return self._create_error_plot(
|
| 343 |
+
"❌ Erreur lors de la création du graphique",
|
| 344 |
+
f"Erreur technique: {str(e)[:100]}..."
|
| 345 |
+
)
|
| 346 |
+
|
| 347 |
+
except Exception as e:
|
| 348 |
+
print(f"❌ Erreur critique dans create_surface_analysis: {e}")
|
| 349 |
+
return self._create_error_plot(
|
| 350 |
+
"❌ Erreur critique",
|
| 351 |
+
"Impossible de créer l'analyse des surfaces"
|
| 352 |
+
)
|