Spaces:
Sleeping
Sleeping
| """ | |
| Module d'interface utilisateur avec Gradio | |
| """ | |
| import os | |
| # Désactiver les analytics Gradio dès le début | |
| os.environ["GRADIO_ANALYTICS_ENABLED"] = "False" | |
| import gradio as gr | |
| from data_loader import DataLoader | |
| from analyzer import AgricultureAnalyzer | |
| from visualizations import AgricultureVisualizer | |
| from config import GRADIO_CONFIG | |
| class AgricultureInterface: | |
| """Classe responsable de l'interface utilisateur Gradio""" | |
| def __init__(self): | |
| self.data_loader = DataLoader() | |
| self.analyzer = AgricultureAnalyzer() | |
| self.visualizer = AgricultureVisualizer() | |
| self._initialize_data() | |
| def _initialize_data(self): | |
| """Initialise les données au démarrage""" | |
| print("🔄 Initialisation des données...") | |
| self.data_loader.load_data() | |
| if self.data_loader.has_data(): | |
| self.analyzer.set_data(self.data_loader.get_data()) | |
| self.analyzer.analyze_data() | |
| self.visualizer.set_data( | |
| self.data_loader.get_data(), | |
| self.analyzer.get_risk_analysis() | |
| ) | |
| print("✅ Initialisation réussie") | |
| else: | |
| print("⚠️ Aucune donnée disponible au démarrage") | |
| def refresh_data(self): | |
| """Rafraîchit toutes les données""" | |
| print("🔄 Rafraîchissement des données...") | |
| self.data_loader.load_data() | |
| if self.data_loader.has_data(): | |
| self.analyzer.set_data(self.data_loader.get_data()) | |
| self.analyzer.analyze_data() | |
| self.visualizer.set_data( | |
| self.data_loader.get_data(), | |
| self.analyzer.get_risk_analysis() | |
| ) | |
| return ( | |
| self._safe_get_summary_stats(), | |
| self._safe_create_culture_analysis(), | |
| self._safe_create_risk_distribution(), | |
| self._safe_create_risk_visualization(), | |
| self._safe_get_recommendations() | |
| ) | |
| else: | |
| return self._get_error_outputs("Aucune donnée disponible") | |
| def _get_error_outputs(self, error_message): | |
| """Retourne des outputs d'erreur standardisés""" | |
| empty_fig = self.visualizer._create_error_plot("❌ Erreur", error_message) | |
| return ( | |
| f"❌ {error_message}", | |
| empty_fig, | |
| empty_fig, | |
| empty_fig, | |
| f"❌ {error_message}" | |
| ) | |
| def _safe_get_summary_stats(self): | |
| """Récupère les statistiques avec gestion d'erreur""" | |
| return self.analyzer.get_summary_stats() | |
| def _safe_create_culture_analysis(self): | |
| """Crée l'analyse des cultures avec gestion d'erreur""" | |
| return self.visualizer.create_culture_analysis() | |
| def _safe_create_risk_distribution(self): | |
| """Crée la distribution des risques avec gestion d'erreur""" | |
| return self.visualizer.create_risk_distribution() | |
| def _safe_create_risk_visualization(self): | |
| """Crée la visualisation des risques avec gestion d'erreur""" | |
| return self.visualizer.create_risk_visualization() | |
| def _safe_get_recommendations(self): | |
| """Récupère les recommandations avec gestion d'erreur""" | |
| return self.analyzer.get_low_risk_recommendations() | |
| def create_interface(self): | |
| """Crée l'interface Gradio""" | |
| with gr.Blocks(title="🌾 Analyse Adventices Agricoles CRA", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # 🌾 Analyse des Adventices Agricoles - CRA Bretagne | |
| **Objectif**: Anticiper et réduire la pression des adventices dans les parcelles agricoles bretonnes | |
| Cette application analyse les données historiques pour identifier les parcelles les plus adaptées | |
| à la culture de plantes sensibles comme le pois ou le haricot. | |
| """) | |
| with gr.Tabs(): | |
| with gr.TabItem("📊 Vue d'ensemble"): | |
| self._create_overview_tab() | |
| with gr.TabItem("🎯 Analyse des Risques"): | |
| self._create_risk_analysis_tab() | |
| with gr.TabItem("🌾 Recommandations"): | |
| self._create_recommendations_tab() | |
| with gr.TabItem("📋 Données par Année"): | |
| self._create_data_viewer_tab() | |
| with gr.TabItem("ℹ️ À propos"): | |
| self._create_about_tab() | |
| # Bouton de rafraîchissement | |
| refresh_btn = gr.Button("🔄 Actualiser les données", variant="secondary") | |
| # Connecter le bouton de rafraîchissement | |
| refresh_btn.click( | |
| self.refresh_data, | |
| outputs=[ | |
| self.stats_output, | |
| self.culture_plot, | |
| self.risk_dist_plot, | |
| self.risk_plot, | |
| self.reco_output | |
| ] | |
| ) | |
| return demo | |
| def _create_overview_tab(self): | |
| """Crée l'onglet de vue d'ensemble""" | |
| gr.Markdown("## Statistiques générales des données agricoles") | |
| self.stats_output = gr.Markdown(self._safe_get_summary_stats()) | |
| with gr.Row(): | |
| self.culture_plot = gr.Plot(self._safe_create_culture_analysis()) | |
| self.risk_dist_plot = gr.Plot(self._safe_create_risk_distribution()) | |
| def _create_risk_analysis_tab(self): | |
| """Crée l'onglet d'analyse des risques""" | |
| gr.Markdown("## Cartographie des risques adventices par parcelle") | |
| self.risk_plot = gr.Plot(self._safe_create_risk_visualization()) | |
| gr.Markdown(""" | |
| **Interprétation du graphique**: | |
| - **Axe X**: Surface de la parcelle (hectares) | |
| - **Axe Y**: IFT Herbicide approximatif | |
| - **Couleur**: Niveau de risque adventice | |
| - **Taille**: Nombre d'herbicides utilisés | |
| Les parcelles vertes (risque faible) sont idéales pour les cultures sensibles. | |
| """) | |
| def _create_recommendations_tab(self): | |
| """Crée l'onglet des recommandations""" | |
| self.reco_output = gr.Markdown(self._safe_get_recommendations()) | |
| gr.Markdown(""" | |
| ## 💡 Conseils pour la gestion des adventices | |
| ### Parcelles à Très Faible Risque (Vertes) | |
| - ✅ **Idéales pour pois et haricot** | |
| - ✅ Historique d'usage herbicide minimal | |
| - ✅ Pression adventice faible attendue | |
| ### Parcelles à Faible Risque (Vert clair) | |
| - ⚠️ Surveillance légère recommandée | |
| - ✅ Conviennent aux cultures sensibles avec précautions | |
| ### Parcelles à Risque Modéré/Élevé (Orange/Rouge) | |
| - ❌ Éviter pour cultures sensibles | |
| - 🔍 Rotation nécessaire avant implantation | |
| - 📈 Surveillance renforcée des adventices | |
| ### Stratégies alternatives | |
| - **Rotation longue**: 3-4 ans avant cultures sensibles | |
| - **Cultures intermédiaires**: CIPAN pour réduire la pression | |
| - **Techniques mécaniques**: Hersage, binage | |
| - **Biostimulants**: Renforcement naturel des cultures | |
| """) | |
| def _create_data_viewer_tab(self): | |
| """Crée l'onglet de visualisation des données par année""" | |
| gr.Markdown("## 📋 Exploration des Données par Année") | |
| # Récupérer les années disponibles | |
| available_years = self.visualizer.get_available_years() | |
| if not available_years: | |
| gr.Markdown("❌ Aucune année disponible dans les données") | |
| return | |
| # Ajouter une option "Toutes années" | |
| year_choices = ["Toutes les années"] + [str(year) for year in available_years] | |
| # Récupérer les parcelles disponibles | |
| available_parcels = self.analyzer.get_available_parcels() | |
| # Interface de sélection | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| year_selector = gr.Dropdown( | |
| choices=year_choices, | |
| value="Toutes les années", | |
| label="🗓️ Sélectionnez une année", | |
| interactive=True | |
| ) | |
| parcel_selector = gr.Dropdown( | |
| choices=[choice[0] for choice in available_parcels], | |
| value="Toutes les parcelles", | |
| label="🏠 Sélectionnez une parcelle", | |
| interactive=True | |
| ) | |
| with gr.Column(scale=1): | |
| update_btn = gr.Button( | |
| "🔄 Actualiser la vue", | |
| variant="primary" | |
| ) | |
| # Informations sur la sélection | |
| self.data_info = gr.Markdown("📊 Sélectionnez une année pour voir les données") | |
| # Graphiques de résumé | |
| with gr.Row(): | |
| with gr.Column(): | |
| self.yearly_summary_plot = gr.Plot( | |
| self._safe_create_yearly_summary(None) | |
| ) | |
| with gr.Column(): | |
| self.monthly_activity_plot = gr.Plot( | |
| self._safe_create_monthly_activity(None) | |
| ) | |
| # Tableau des données | |
| gr.Markdown("### 📋 Tableau des Données") | |
| self.data_table = gr.Dataframe( | |
| value=self._safe_create_data_table(None)[0], | |
| label="Données de la période sélectionnée", | |
| interactive=False, | |
| wrap=True | |
| ) | |
| # Fonction de mise à jour des parcelles disponibles selon l'année | |
| def update_parcel_choices(selected_year): | |
| year = None if selected_year == "Toutes les années" else int(selected_year) | |
| parcels_for_year = self.analyzer.get_available_parcels_for_year(year) | |
| parcel_choices = [choice[0] for choice in parcels_for_year] | |
| return gr.Dropdown(choices=parcel_choices, value="Toutes les parcelles") | |
| # Fonction de mise à jour des années disponibles selon la parcelle | |
| def update_year_choices(selected_parcel): | |
| # Convertir la sélection de parcelle en ID | |
| parcel_id = None | |
| if selected_parcel != "Toutes les parcelles": | |
| current_parcels = self.analyzer.get_available_parcels() | |
| for choice in current_parcels: | |
| if choice[0] == selected_parcel: | |
| parcel_id = choice[1] if choice[1] != "ALL" else None | |
| break | |
| years_for_parcel = self.analyzer.get_available_years_for_parcel(parcel_id) | |
| return gr.Dropdown(choices=years_for_parcel, value="Toutes les années") | |
| # Fonction de mise à jour des données | |
| def update_data_view(selected_year, selected_parcel): | |
| # Convertir les sélections | |
| year = None if selected_year == "Toutes les années" else int(selected_year) | |
| # Convertir la sélection de parcelle | |
| parcel_id = None | |
| if selected_parcel != "Toutes les parcelles": | |
| # Récupérer les parcelles à jour pour éviter les problèmes de cache | |
| current_parcels = self.analyzer.get_available_parcels() | |
| # Récupérer l'ID de la parcelle depuis les données disponibles | |
| for choice in current_parcels: | |
| if choice[0] == selected_parcel: | |
| parcel_id = choice[1] if choice[1] != "ALL" else None | |
| break | |
| # Générer les nouvelles vues | |
| data_table, info_msg = self._safe_create_data_table(year, parcel_id) | |
| summary_plot = self._safe_create_yearly_summary(year, parcel_id) | |
| monthly_plot = self._safe_create_monthly_activity(year, parcel_id) | |
| return ( | |
| info_msg, | |
| summary_plot, | |
| monthly_plot, | |
| data_table | |
| ) | |
| # Connecter les événements pour les dropdowns dynamiques | |
| year_selector.change( | |
| update_parcel_choices, | |
| inputs=[year_selector], | |
| outputs=[parcel_selector] | |
| ).then( | |
| update_data_view, | |
| inputs=[year_selector, parcel_selector], | |
| outputs=[ | |
| self.data_info, | |
| self.yearly_summary_plot, | |
| self.monthly_activity_plot, | |
| self.data_table | |
| ] | |
| ) | |
| parcel_selector.change( | |
| update_year_choices, | |
| inputs=[parcel_selector], | |
| outputs=[year_selector] | |
| ).then( | |
| update_data_view, | |
| inputs=[year_selector, parcel_selector], | |
| outputs=[ | |
| self.data_info, | |
| self.yearly_summary_plot, | |
| self.monthly_activity_plot, | |
| self.data_table | |
| ] | |
| ) | |
| update_btn.click( | |
| update_data_view, | |
| inputs=[year_selector, parcel_selector], | |
| outputs=[ | |
| self.data_info, | |
| self.yearly_summary_plot, | |
| self.monthly_activity_plot, | |
| self.data_table | |
| ] | |
| ) | |
| gr.Markdown(""" | |
| ### 💡 Utilisation de l'onglet Données | |
| - **Sélection d'année** : Filtrez les données par millésime | |
| - **Sélection de parcelle** : Filtrez les données par parcelle spécifique | |
| - **Filtrage intelligent** : Les choix se mettent à jour automatiquement selon votre sélection | |
| - **Graphique des interventions** : Types d'interventions les plus fréquents | |
| - **Activité mensuelle** : Répartition des interventions par mois | |
| - **Tableau détaillé** : Données brutes avec colonnes importantes | |
| > 📝 **Note** : Le tableau est limité à 1000 lignes pour des raisons de performance | |
| > 🔄 **Astuce** : Les listes se mettent à jour dynamiquement - sélectionnez une année pour voir ses parcelles ! | |
| """) | |
| def _safe_create_data_table(self, year, parcel_id=None): | |
| """Crée le tableau de données""" | |
| if parcel_id is not None: | |
| # Utiliser la méthode qui prend en compte les parcelles | |
| return self.analyzer.get_data_table_by_year_and_parcel(year, parcel_id) | |
| else: | |
| return self.visualizer.create_data_table_by_year(year) | |
| def _safe_create_yearly_summary(self, year, parcel_id=None): | |
| """Crée le résumé annuel""" | |
| if parcel_id is not None: | |
| # Pour l'instant, utiliser la méthode existante car les méthodes du visualizer n'ont pas encore le support parcelle | |
| return self.visualizer.create_yearly_summary_chart(year) | |
| else: | |
| return self.visualizer.create_yearly_summary_chart(year) | |
| def _safe_create_monthly_activity(self, year, parcel_id=None): | |
| """Crée l'activité mensuelle""" | |
| if parcel_id is not None: | |
| # Pour l'instant, utiliser la méthode existante car les méthodes du visualizer n'ont pas encore le support parcelle | |
| return self.visualizer.create_monthly_activity_chart(year) | |
| else: | |
| return self.visualizer.create_monthly_activity_chart(year) | |
| def _create_about_tab(self): | |
| """Crée l'onglet à propos""" | |
| gr.Markdown(""" | |
| ## 🎯 Méthodologie | |
| Cette analyse se base sur : | |
| ### Calcul de l'IFT (Indice de Fréquence de Traitement) | |
| - **IFT ≈ Quantité appliquée / Surface de parcelle** | |
| - Indicateur de l'intensité des traitements herbicides | |
| ### Classification des risques | |
| - **TRÈS FAIBLE**: IFT = 0, aucun herbicide | |
| - **FAIBLE**: IFT < 1, usage minimal | |
| - **MODÉRÉ**: IFT < 3, usage modéré | |
| - **ÉLEVÉ**: IFT < 5, usage important | |
| - **TRÈS ÉLEVÉ**: IFT ≥ 5, usage intensif | |
| ### Données analysées | |
| - **Source**: Station Expérimentale de Kerguéhennec | |
| - **Période**: Campagne 2025 | |
| - **Variables**: Interventions, produits, quantités, surfaces | |
| --- | |
| **Développé pour le Hackathon CRA Bretagne** 🏆 | |
| *Application d'aide à la décision pour une agriculture durable* | |
| """) | |
| def launch(self): | |
| """Lance l'interface""" | |
| demo = self.create_interface() | |
| demo.launch(**GRADIO_CONFIG) | |