Spaces:
Sleeping
Sleeping
| """ | |
| Gradio interface for the Agricultural MCP Server. | |
| Provides a web interface for interacting with agricultural data analysis tools. | |
| """ | |
| import gradio as gr | |
| import json | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| import os | |
| from data_loader import AgriculturalDataLoader | |
| from analysis_tools import AgriculturalAnalyzer | |
| # Initialize components | |
| # Use Hugging Face dataset exclusively | |
| data_loader = AgriculturalDataLoader() | |
| print("🤗 Configured to use Hugging Face dataset exclusively") | |
| analyzer = AgriculturalAnalyzer(data_loader) | |
| # Global state for data | |
| def load_initial_data(): | |
| """Load and cache initial data.""" | |
| try: | |
| df = data_loader.load_all_files() | |
| return df | |
| except Exception as e: | |
| print(f"Error loading data: {e}") | |
| return pd.DataFrame() | |
| def get_data_summary(): | |
| """Get summary of the agricultural data.""" | |
| try: | |
| df = load_initial_data() | |
| if df.empty: | |
| return "Aucune donnée disponible" | |
| summary = f""" | |
| ## Résumé des Données Agricoles - Station Expérimentale de Kerguéhennec | |
| 📊 **Statistiques Générales:** | |
| - **Total d'enregistrements:** {len(df):,} | |
| - **Parcelles uniques:** {df['plot_name'].nunique()} | |
| - **Types de cultures:** {df['crop_type'].nunique()} | |
| - **Années couvertes:** {', '.join(map(str, sorted(df['year'].unique())))} | |
| - **Applications herbicides:** {len(df[df['is_herbicide'] == True]):,} | |
| 🌱 **Cultures principales:** | |
| {df['crop_type'].value_counts().head(5).to_string()} | |
| 📍 **Parcelles principales:** | |
| {df['plot_name'].value_counts().head(5).to_string()} | |
| """ | |
| return summary | |
| except Exception as e: | |
| return f"Erreur lors du chargement des données: {str(e)}" | |
| def filter_and_analyze_data(years, plots, crops): | |
| """Filter data and provide analysis.""" | |
| try: | |
| df = load_initial_data() | |
| if df.empty: | |
| return "Aucune donnée disponible", None | |
| # Convert inputs to lists if not None | |
| year_list = [int(y) for y in years] if years else None | |
| plot_list = plots if plots else None | |
| crop_list = crops if crops else None | |
| # Filter data | |
| filtered_df = data_loader.filter_data( | |
| years=year_list, | |
| plots=plot_list, | |
| crops=crop_list | |
| ) | |
| if filtered_df.empty: | |
| return "Aucune donnée trouvée avec ces filtres", None | |
| # Generate analysis | |
| analysis = f""" | |
| ## Analyse des Données Filtrées | |
| **Filtres appliqués:** | |
| - Années: {years if years else 'Toutes'} | |
| - Parcelles: {', '.join(plots) if plots else 'Toutes'} | |
| - Cultures: {', '.join(crops) if crops else 'Toutes'} | |
| **Résultats:** | |
| - Enregistrements filtrés: {len(filtered_df):,} | |
| - Applications herbicides: {len(filtered_df[filtered_df['is_herbicide'] == True]):,} | |
| - Parcelles concernées: {filtered_df['plot_name'].nunique()} | |
| - Cultures concernées: {filtered_df['crop_type'].nunique()} | |
| **Distribution par année:** | |
| {filtered_df['year'].value_counts().sort_index().to_string()} | |
| """ | |
| # Create visualization | |
| yearly_dist = filtered_df['year'].value_counts().sort_index() | |
| fig = px.bar( | |
| x=yearly_dist.index, | |
| y=yearly_dist.values, | |
| title="Distribution des Interventions par Année", | |
| labels={'x': 'Année', 'y': 'Nombre d\'Interventions'} | |
| ) | |
| return analysis, fig | |
| except Exception as e: | |
| return f"Erreur lors de l'analyse: {str(e)}", None | |
| def analyze_weed_pressure(years, plots): | |
| """Analyze weed pressure trends.""" | |
| try: | |
| # Convert inputs | |
| year_list = [int(y) for y in years] if years else None | |
| plot_list = plots if plots else None | |
| # Get analysis | |
| trends = analyzer.analyze_weed_pressure_trends(years=year_list, plots=plot_list) | |
| # Format results | |
| summary_stats = trends['summary'] | |
| analysis_text = f""" | |
| ## Analyse de la Pression Adventices (IFT Herbicides) | |
| **Statistiques globales:** | |
| - IFT moyen: {summary_stats['mean_ift']:.2f} | |
| - Écart-type: {summary_stats['std_ift']:.2f} | |
| - IFT minimum: {summary_stats['min_ift']:.2f} | |
| - IFT maximum: {summary_stats['max_ift']:.2f} | |
| - Total applications: {summary_stats['total_applications']} | |
| - Parcelles analysées: {summary_stats['unique_plots']} | |
| - Cultures analysées: {summary_stats['unique_crops']} | |
| **Interprétation:** | |
| - IFT < 1.0: Pression faible (adapté aux cultures sensibles) | |
| - IFT 1.0-2.0: Pression modérée | |
| - IFT > 2.0: Pression élevée | |
| """ | |
| # Create visualization | |
| fig = analyzer.create_weed_pressure_visualization(years=year_list, plots=plot_list) | |
| return analysis_text, fig | |
| except Exception as e: | |
| return f"Erreur lors de l'analyse de pression: {str(e)}", None | |
| def predict_future_weed_pressure(target_years, max_ift): | |
| """Predict weed pressure for future years.""" | |
| try: | |
| # Convert target years | |
| year_list = [int(y) for y in target_years] if target_years else [2025, 2026, 2027] | |
| # Get predictions | |
| predictions = analyzer.predict_weed_pressure(target_years=year_list) | |
| # Format results | |
| model_perf = predictions['model_performance'] | |
| results_text = f""" | |
| ## Prédiction de la Pression Adventices | |
| **Performance du modèle:** | |
| - R² Score: {model_perf['r2']:.3f} | |
| - Erreur quadratique moyenne: {model_perf['mse']:.3f} | |
| **Prédictions par année:** | |
| """ | |
| # Add predictions for each year | |
| prediction_data = [] | |
| for year in year_list: | |
| if year in predictions['predictions']: | |
| year_pred = predictions['predictions'][year] | |
| results_text += f"\n**{year}:**\n" | |
| for _, row in year_pred.iterrows(): | |
| results_text += f"- {row['plot_name']}: IFT {row['predicted_ift']:.2f} (Risque: {row['risk_level']})\n" | |
| prediction_data.append({ | |
| 'Année': year, | |
| 'Parcelle': row['plot_name'], | |
| 'IFT_Prédit': row['predicted_ift'], | |
| 'Niveau_Risque': row['risk_level'] | |
| }) | |
| # Identify suitable plots | |
| suitable_plots = analyzer.identify_suitable_plots_for_sensitive_crops( | |
| target_years=year_list, | |
| max_ift_threshold=max_ift | |
| ) | |
| results_text += f"\n\n**Parcelles adaptées aux cultures sensibles (IFT < {max_ift}):**\n" | |
| for year, plots in suitable_plots.items(): | |
| if plots: | |
| results_text += f"- {year}: {', '.join(plots)}\n" | |
| else: | |
| results_text += f"- {year}: Aucune parcelle adaptée\n" | |
| # Create visualization | |
| if prediction_data: | |
| pred_df = pd.DataFrame(prediction_data) | |
| fig = px.scatter( | |
| pred_df, | |
| x='Année', | |
| y='IFT_Prédit', | |
| color='Niveau_Risque', | |
| size='IFT_Prédit', | |
| hover_data=['Parcelle'], | |
| title="Prédictions IFT par Parcelle et Année", | |
| color_discrete_map={'low': 'green', 'medium': 'orange', 'high': 'red'} | |
| ) | |
| fig.add_hline(y=max_ift, line_dash="dash", line_color="red", | |
| annotation_text=f"Seuil cultures sensibles ({max_ift})") | |
| return results_text, fig | |
| else: | |
| return results_text, None | |
| except Exception as e: | |
| return f"Erreur lors de la prédiction: {str(e)}", None | |
| def analyze_crop_rotation(): | |
| """Analyze crop rotation impact.""" | |
| try: | |
| rotation_impact = analyzer.analyze_crop_rotation_impact() | |
| if rotation_impact.empty: | |
| return "Pas assez de données pour analyser les rotations", None | |
| analysis_text = f""" | |
| ## Impact des Rotations sur la Pression Adventices | |
| **Rotations les plus favorables (IFT moyen le plus bas):** | |
| """ | |
| # Show top 10 best rotations | |
| best_rotations = rotation_impact.head(10) | |
| for _, row in best_rotations.iterrows(): | |
| analysis_text += f"\n- **{row['rotation_type']}**" | |
| analysis_text += f"\n - IFT moyen: {row['mean_ift']:.2f}" | |
| analysis_text += f"\n - Écart-type: {row['std_ift']:.2f}" | |
| analysis_text += f"\n - Observations: {row['count']}\n" | |
| # Create visualization | |
| top_20 = rotation_impact.head(20) | |
| fig = px.bar( | |
| top_20, | |
| x='mean_ift', | |
| y='rotation_type', | |
| orientation='h', | |
| title="Impact des Rotations sur l'IFT Herbicide (Top 20)", | |
| labels={'mean_ift': 'IFT Moyen', 'rotation_type': 'Type de Rotation'}, | |
| color='mean_ift', | |
| color_continuous_scale='RdYlGn_r' | |
| ) | |
| fig.update_layout(height=800) | |
| return analysis_text, fig | |
| except Exception as e: | |
| return f"Erreur lors de l'analyse des rotations: {str(e)}", None | |
| def analyze_herbicide_usage(): | |
| """Analyze herbicide usage patterns.""" | |
| try: | |
| herbicide_analysis = analyzer.analyze_herbicide_alternatives() | |
| analysis_text = f""" | |
| ## Analyse des Herbicides Utilisés | |
| **Herbicides les plus utilisés:** | |
| """ | |
| top_herbicides = herbicide_analysis.head(15) | |
| for _, row in top_herbicides.iterrows(): | |
| analysis_text += f"\n- **{row['produit']}** ({row['crop_type']})" | |
| analysis_text += f"\n - Applications: {row['applications']}" | |
| analysis_text += f"\n - Quantité totale: {row['total_quantity']:.1f}" | |
| analysis_text += f"\n - Quantité moyenne: {row['avg_quantity']:.1f}" | |
| if not pd.isna(row['amm_code']): | |
| analysis_text += f"\n - Code AMM: {row['amm_code']}" | |
| analysis_text += "\n" | |
| # Create visualization | |
| fig = px.bar( | |
| top_herbicides.head(10), | |
| x='applications', | |
| y='produit', | |
| orientation='h', | |
| title="Herbicides les Plus Utilisés (Nombre d'Applications)", | |
| labels={'applications': 'Nombre d\'Applications', 'produit': 'Produit'}, | |
| color='applications' | |
| ) | |
| fig.update_layout(height=600) | |
| return analysis_text, fig | |
| except Exception as e: | |
| return f"Erreur lors de l'analyse des herbicides: {str(e)}", None | |
| # Create Gradio interface | |
| def create_gradio_app(): | |
| """Create the Gradio application.""" | |
| # Load data for dropdowns | |
| try: | |
| df = load_initial_data() | |
| available_years = sorted(df['year'].unique()) if not df.empty else [] | |
| available_plots = sorted(df['plot_name'].unique()) if not df.empty else [] | |
| available_crops = sorted(df['crop_type'].unique()) if not df.empty else [] | |
| except: | |
| available_years = [] | |
| available_plots = [] | |
| available_crops = [] | |
| with gr.Blocks(title="🚜 Analyse Agricole - Station de Kerguéhennec", theme=gr.themes.Soft()) as app: | |
| gr.Markdown(""" | |
| # 🚜 Analyse des Données Agricoles | |
| ## Station Expérimentale de Kerguéhennec | |
| ### Outil d'aide à la décision pour la réduction des herbicides et l'identification des parcelles adaptées aux cultures sensibles | |
| """) | |
| with gr.Tabs(): | |
| # Tab 1: Data Overview | |
| with gr.Tab("📊 Aperçu des Données"): | |
| gr.Markdown("## Résumé des données disponibles") | |
| summary_output = gr.Markdown(value=get_data_summary()) | |
| refresh_btn = gr.Button("🔄 Actualiser", variant="secondary") | |
| refresh_btn.click(get_data_summary, outputs=summary_output) | |
| # Tab 2: Data Filtering | |
| with gr.Tab("🔍 Filtrage et Exploration"): | |
| gr.Markdown("## Filtrer et explorer les données") | |
| with gr.Row(): | |
| with gr.Column(): | |
| years_filter = gr.CheckboxGroup( | |
| choices=[str(y) for y in available_years], | |
| label="Années", | |
| value=[str(y) for y in available_years[-3:]] if available_years else [] | |
| ) | |
| plots_filter = gr.CheckboxGroup( | |
| choices=available_plots, | |
| label="Parcelles", | |
| value=available_plots[:5] if available_plots else [] | |
| ) | |
| crops_filter = gr.CheckboxGroup( | |
| choices=available_crops, | |
| label="Cultures", | |
| value=available_crops[:5] if available_crops else [] | |
| ) | |
| analyze_btn = gr.Button("📈 Analyser", variant="primary") | |
| with gr.Column(): | |
| filter_results = gr.Markdown() | |
| filter_plot = gr.Plot() | |
| analyze_btn.click( | |
| filter_and_analyze_data, | |
| inputs=[years_filter, plots_filter, crops_filter], | |
| outputs=[filter_results, filter_plot] | |
| ) | |
| # Tab 3: Weed Pressure Analysis | |
| with gr.Tab("🌿 Pression Adventices"): | |
| gr.Markdown("## Analyse de la pression adventices (IFT Herbicides)") | |
| with gr.Row(): | |
| with gr.Column(): | |
| years_pressure = gr.CheckboxGroup( | |
| choices=[str(y) for y in available_years], | |
| label="Années à analyser", | |
| value=[str(y) for y in available_years] if available_years else [] | |
| ) | |
| plots_pressure = gr.CheckboxGroup( | |
| choices=available_plots, | |
| label="Parcelles à analyser", | |
| value=available_plots if len(available_plots) <= 10 else available_plots[:10] | |
| ) | |
| pressure_btn = gr.Button("🔬 Analyser la Pression", variant="primary") | |
| with gr.Column(): | |
| pressure_results = gr.Markdown() | |
| pressure_plot = gr.Plot() | |
| pressure_btn.click( | |
| analyze_weed_pressure, | |
| inputs=[years_pressure, plots_pressure], | |
| outputs=[pressure_results, pressure_plot] | |
| ) | |
| # Tab 4: Predictions | |
| with gr.Tab("🔮 Prédictions"): | |
| gr.Markdown("## Prédiction de la pression adventices") | |
| with gr.Row(): | |
| with gr.Column(): | |
| target_years = gr.CheckboxGroup( | |
| choices=["2025", "2026", "2027"], | |
| label="Années à prédire", | |
| value=["2025", "2026", "2027"] | |
| ) | |
| max_ift = gr.Slider( | |
| minimum=0.5, | |
| maximum=3.0, | |
| value=1.0, | |
| step=0.1, | |
| label="Seuil IFT max pour cultures sensibles" | |
| ) | |
| predict_btn = gr.Button("🎯 Prédire", variant="primary") | |
| with gr.Column(): | |
| prediction_results = gr.Markdown() | |
| prediction_plot = gr.Plot() | |
| predict_btn.click( | |
| predict_future_weed_pressure, | |
| inputs=[target_years, max_ift], | |
| outputs=[prediction_results, prediction_plot] | |
| ) | |
| # Tab 5: Crop Rotation | |
| with gr.Tab("🔄 Rotations"): | |
| gr.Markdown("## Impact des rotations culturales") | |
| rotation_btn = gr.Button("📊 Analyser les Rotations", variant="primary") | |
| rotation_results = gr.Markdown() | |
| rotation_plot = gr.Plot() | |
| rotation_btn.click( | |
| analyze_crop_rotation, | |
| outputs=[rotation_results, rotation_plot] | |
| ) | |
| # Tab 6: Herbicide Analysis | |
| with gr.Tab("💊 Herbicides"): | |
| gr.Markdown("## Analyse des herbicides utilisés") | |
| herbicide_btn = gr.Button("🧪 Analyser les Herbicides", variant="primary") | |
| herbicide_results = gr.Markdown() | |
| herbicide_plot = gr.Plot() | |
| herbicide_btn.click( | |
| analyze_herbicide_usage, | |
| outputs=[herbicide_results, herbicide_plot] | |
| ) | |
| gr.Markdown(""" | |
| --- | |
| **Note:** Cet outil utilise les données historiques d'interventions de la Station Expérimentale de Kerguéhennec | |
| pour analyser la pression adventices et identifier les parcelles les plus adaptées aux cultures sensibles | |
| comme le pois et le haricot. | |
| """) | |
| return app | |
| # Launch the app | |
| if __name__ == "__main__": | |
| app = create_gradio_app() | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=True, | |
| debug=True | |
| ) | |