Tracy André commited on
Commit
6df7dd5
·
1 Parent(s): 5ddad7c
Files changed (2) hide show
  1. mcp_server.py +232 -32
  2. test_improved_interface.py +44 -0
mcp_server.py CHANGED
@@ -90,7 +90,23 @@ class WeedPressureAnalyzer:
90
  analyzer = WeedPressureAnalyzer()
91
 
92
  def analyze_herbicide_trends(year_start, year_end, plot_filter):
93
- """Analyze herbicide usage trends over time."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  try:
95
  # Créer la liste des années à partir des deux sliders
96
  start_year = int(year_start)
@@ -181,7 +197,25 @@ def analyze_herbicide_trends(year_start, year_end, plot_filter):
181
  return None, error_msg
182
 
183
  def predict_future_weed_pressure():
184
- """Predict weed pressure for the next 3 years."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  try:
186
  predictions = analyzer.predict_weed_pressure()
187
 
@@ -215,7 +249,29 @@ def predict_future_weed_pressure():
215
  return None, f"Erreur: {str(e)}"
216
 
217
  def recommend_sensitive_crop_plots():
218
- """Recommend plots for sensitive crops."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  try:
220
  predictions = analyzer.predict_weed_pressure()
221
 
@@ -253,34 +309,90 @@ def recommend_sensitive_crop_plots():
253
  except Exception as e:
254
  return None, f"Erreur: {str(e)}"
255
 
256
- def generate_technical_alternatives(herbicide_family):
257
- """Generate technical alternatives."""
258
- summary = f"""
259
- 🔄 **Alternatives aux {herbicide_family}**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
- **🚜 Alternatives Mécaniques:**
262
- • Faux-semis répétés avant implantation
263
- Binage mécanique en inter-rang
264
- Herse étrille en post-levée précoce
 
265
 
266
- **🌾 Alternatives Culturales:**
267
- Rotation longue avec prairie temporaire
268
- Cultures intermédiaires piège à nitrates
269
- Densité de semis optimisée
 
 
270
 
271
- **🧪 Alternatives Biologiques:**
272
- • Stimulateurs de défenses naturelles
273
- • Extraits végétaux (huiles essentielles)
274
- • Bioherbicides à base de champignons
275
 
276
- **📋 Plan d'Action:**
277
- 1. Tester sur petites surfaces
278
- 2. Former les équipes
279
- 3. Suivre l'efficacité
280
- 4. Documenter les résultats
281
- """
282
-
283
- return summary
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
  def get_available_plots():
286
  """Get available plots."""
@@ -292,6 +404,26 @@ def get_available_plots():
292
  print(f"Erreur lors du chargement des parcelles: {e}")
293
  return ["Toutes", "Champ ferme Bas", "Etang Milieu", "Lann Chebot"]
294
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  # Create Gradio Interface
296
  def create_mcp_interface():
297
  with gr.Blocks(title="🚜 Analyse Pression Adventices", theme=gr.themes.Soft()) as demo:
@@ -304,6 +436,14 @@ def create_mcp_interface():
304
  with gr.Tabs():
305
  with gr.Tab("📈 Analyse Tendances"):
306
  gr.Markdown("### Analyser l'évolution de l'IFT herbicides par parcelle et période")
 
 
 
 
 
 
 
 
307
 
308
  with gr.Row():
309
  with gr.Column():
@@ -343,6 +483,18 @@ def create_mcp_interface():
343
  )
344
 
345
  with gr.Tab("🔮 Prédictions"):
 
 
 
 
 
 
 
 
 
 
 
 
346
  predict_btn = gr.Button("🎯 Prédire 2025-2027", variant="primary")
347
 
348
  with gr.Row():
@@ -352,6 +504,15 @@ def create_mcp_interface():
352
  predict_btn.click(predict_future_weed_pressure, outputs=[predictions_plot, predictions_summary])
353
 
354
  with gr.Tab("🌱 Recommandations"):
 
 
 
 
 
 
 
 
 
355
  recommend_btn = gr.Button("🎯 Recommander Parcelles", variant="primary")
356
 
357
  with gr.Row():
@@ -360,12 +521,51 @@ def create_mcp_interface():
360
 
361
  recommend_btn.click(recommend_sensitive_crop_plots, outputs=[recommendations_plot, recommendations_summary])
362
 
363
- with gr.Tab("🔄 Alternatives"):
364
- herbicide_type = gr.Dropdown(["Herbicides", "Fongicides"], value="Herbicides", label="Type")
365
- alternatives_btn = gr.Button("💡 Générer Alternatives", variant="primary")
366
- alternatives_output = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
 
368
- alternatives_btn.click(generate_technical_alternatives, [herbicide_type], [alternatives_output])
 
 
 
 
369
 
370
  return demo
371
 
 
90
  analyzer = WeedPressureAnalyzer()
91
 
92
  def analyze_herbicide_trends(year_start, year_end, plot_filter):
93
+ """
94
+ Analyze herbicide usage trends over time by calculating IFT (Treatment Frequency Index).
95
+
96
+ This tool calculates the IFT (Indice de Fréquence de Traitement) for herbicides, which represents
97
+ the number of herbicide applications per hectare. It provides visualizations and statistics to
98
+ understand weed pressure evolution over time.
99
+
100
+ Args:
101
+ year_start (int): Starting year for analysis (2014-2025)
102
+ year_end (int): Ending year for analysis (2014-2025)
103
+ plot_filter (str): Specific plot name or "Toutes" for all plots
104
+
105
+ Returns:
106
+ tuple: (plotly_figure, markdown_summary)
107
+ - plotly_figure: Interactive line chart showing IFT evolution by plot and year
108
+ - markdown_summary: Detailed statistics including mean/max IFT, risk distribution
109
+ """
110
  try:
111
  # Créer la liste des années à partir des deux sliders
112
  start_year = int(year_start)
 
197
  return None, error_msg
198
 
199
  def predict_future_weed_pressure():
200
+ """
201
+ Predict weed pressure for the next 3 years (2025-2027) using linear regression on historical IFT data.
202
+
203
+ This tool uses historical herbicide IFT data to predict future weed pressure. It applies linear
204
+ regression to each plot's IFT evolution over time and extrapolates to 2025-2027. Risk levels are
205
+ classified as: Faible (IFT < 1.0), Modéré (1.0 ≤ IFT < 2.0), Élevé (IFT ≥ 2.0).
206
+
207
+ Prediction Method:
208
+ 1. Calculate historical IFT for each plot/year combination
209
+ 2. Apply linear regression: IFT = slope × year + intercept
210
+ 3. Extrapolate to target years 2025-2027
211
+ 4. Classify risk levels based on predicted IFT values
212
+ 5. Include recent crop history and average historical IFT for context
213
+
214
+ Returns:
215
+ tuple: (plotly_figure, markdown_summary)
216
+ - plotly_figure: Bar chart showing predicted IFT by plot and year with risk color coding
217
+ - markdown_summary: Risk distribution statistics and interpretation
218
+ """
219
  try:
220
  predictions = analyzer.predict_weed_pressure()
221
 
 
249
  return None, f"Erreur: {str(e)}"
250
 
251
  def recommend_sensitive_crop_plots():
252
+ """
253
+ Recommend plots suitable for sensitive crops (pois, haricot) based on predicted weed pressure.
254
+
255
+ This tool identifies plots with low predicted weed pressure (IFT < 1.0) and calculates a
256
+ recommendation score to rank them for sensitive crop cultivation.
257
+
258
+ Recommendation Method:
259
+ 1. Get predicted IFT for 2025-2027 from predict_future_weed_pressure()
260
+ 2. Filter plots with risk_level = "Faible" (IFT < 1.0)
261
+ 3. Calculate recommendation_score = 100 - (predicted_ift × 30)
262
+ 4. Sort plots by recommendation score (higher = better)
263
+ 5. Include recent crop history and historical average IFT for context
264
+
265
+ Recommendation Score:
266
+ - 100-70: Excellent for sensitive crops
267
+ - 70-50: Good for sensitive crops with monitoring
268
+ - 50-0: Requires careful management
269
+
270
+ Returns:
271
+ tuple: (plotly_figure, markdown_summary)
272
+ - plotly_figure: Scatter plot showing predicted IFT vs recommendation score
273
+ - markdown_summary: Top recommended plots with scores and criteria
274
+ """
275
  try:
276
  predictions = analyzer.predict_weed_pressure()
277
 
 
309
  except Exception as e:
310
  return None, f"Erreur: {str(e)}"
311
 
312
+ def explore_raw_data(year_start, year_end, plot_filter, crop_filter, intervention_filter):
313
+ """
314
+ Explore raw agricultural intervention data with filtering capabilities.
315
+
316
+ This tool provides access to the raw dataset from the Station Expérimentale de Kerguéhennec
317
+ (2014-2025) with filtering options to explore specific subsets of data.
318
+
319
+ Args:
320
+ year_start (int): Starting year for filtering (2014-2025)
321
+ year_end (int): Ending year for filtering (2014-2025)
322
+ plot_filter (str): Specific plot name or "Toutes" for all plots
323
+ crop_filter (str): Specific crop type or "Toutes" for all crops
324
+ intervention_filter (str): Specific intervention type or "Toutes" for all interventions
325
+
326
+ Returns:
327
+ tuple: (plotly_figure, markdown_summary)
328
+ - plotly_figure: Interactive data table or visualization
329
+ - markdown_summary: Data summary with statistics and filtering info
330
+ """
331
+ try:
332
+ # Charger les données
333
+ df = analyzer.load_data()
334
+
335
+ # Appliquer les filtres
336
+ if year_start and year_end:
337
+ df = df[(df['year'] >= year_start) & (df['year'] <= year_end)]
338
+
339
+ if plot_filter and plot_filter != "Toutes":
340
+ df = df[df['plot_name'] == plot_filter]
341
+
342
+ if crop_filter and crop_filter != "Toutes":
343
+ df = df[df['crop_type'] == crop_filter]
344
+
345
+ if intervention_filter and intervention_filter != "Toutes":
346
+ df = df[df['intervention_type'] == intervention_filter]
347
+
348
+ if len(df) == 0:
349
+ return None, "Aucune donnée trouvée avec les filtres sélectionnés."
350
+
351
+ # Créer un résumé des données
352
+ summary = f"""
353
+ 📊 **Exploration des Données Brutes**
354
 
355
+ **Filtres appliqués:**
356
+ - Période: {year_start}-{year_end}
357
+ - Parcelle: {plot_filter}
358
+ - Culture: {crop_filter}
359
+ - Type d'intervention: {intervention_filter}
360
 
361
+ **Statistiques:**
362
+ - Nombre total d'enregistrements: {len(df):,}
363
+ - Nombre de parcelles: {df['plot_name'].nunique()}
364
+ - Nombre d'années: {df['year'].nunique()}
365
+ - Types de cultures: {df['crop_type'].nunique()}
366
+ - Types d'interventions: {df['intervention_type'].nunique()}
367
 
368
+ **Répartition par année:**
369
+ {df['year'].value_counts().sort_index().to_string()}
 
 
370
 
371
+ **Top 10 parcelles:**
372
+ {df['plot_name'].value_counts().head(10).to_string()}
373
+
374
+ **Top 10 cultures:**
375
+ {df['crop_type'].value_counts().head(10).to_string()}
376
+
377
+ **Top 10 interventions:**
378
+ {df['intervention_type'].value_counts().head(10).to_string()}
379
+ """
380
+
381
+ # Créer une visualisation des données
382
+ if len(df) > 0:
383
+ # Graphique des interventions par année
384
+ yearly_counts = df.groupby('year').size().reset_index(name='count')
385
+ fig = px.bar(yearly_counts, x='year', y='count',
386
+ title=f'Nombre d\'interventions par année ({year_start}-{year_end})',
387
+ labels={'count': 'Nombre d\'interventions', 'year': 'Année'})
388
+
389
+ fig.update_layout(height=400)
390
+ return fig, summary
391
+ else:
392
+ return None, summary
393
+
394
+ except Exception as e:
395
+ return None, f"Erreur lors de l'exploration des données: {str(e)}"
396
 
397
  def get_available_plots():
398
  """Get available plots."""
 
404
  print(f"Erreur lors du chargement des parcelles: {e}")
405
  return ["Toutes", "Champ ferme Bas", "Etang Milieu", "Lann Chebot"]
406
 
407
+ def get_available_crops():
408
+ """Get available crop types."""
409
+ try:
410
+ df = analyzer.load_data()
411
+ crops = sorted(df['crop_type'].dropna().unique().tolist())
412
+ return ["Toutes"] + crops
413
+ except Exception as e:
414
+ print(f"Erreur lors du chargement des cultures: {e}")
415
+ return ["Toutes", "blé tendre hiver", "pois de conserve", "haricot mange-tout industrie"]
416
+
417
+ def get_available_interventions():
418
+ """Get available intervention types."""
419
+ try:
420
+ df = analyzer.load_data()
421
+ interventions = sorted(df['intervention_type'].dropna().unique().tolist())
422
+ return ["Toutes"] + interventions
423
+ except Exception as e:
424
+ print(f"Erreur lors du chargement des interventions: {e}")
425
+ return ["Toutes", "Traitement et protection des cultures", "Fertilisation", "Travail et Entretien du sol"]
426
+
427
  # Create Gradio Interface
428
  def create_mcp_interface():
429
  with gr.Blocks(title="🚜 Analyse Pression Adventices", theme=gr.themes.Soft()) as demo:
 
436
  with gr.Tabs():
437
  with gr.Tab("📈 Analyse Tendances"):
438
  gr.Markdown("### Analyser l'évolution de l'IFT herbicides par parcelle et période")
439
+ gr.Markdown("""
440
+ **Calcul de l'IFT (Indice de Fréquence de Traitement) :**
441
+ - IFT = Nombre d'applications herbicides / Surface de la parcelle
442
+ - Seuils d'interprétation :
443
+ - 🟢 Faible : IFT < 1.0 (pression adventices faible)
444
+ - 🟡 Modéré : 1.0 ≤ IFT < 2.0 (pression modérée)
445
+ - 🔴 Élevé : IFT ≥ 2.0 (pression élevée)
446
+ """)
447
 
448
  with gr.Row():
449
  with gr.Column():
 
483
  )
484
 
485
  with gr.Tab("🔮 Prédictions"):
486
+ gr.Markdown("### Prédiction de la pression adventices 2025-2027")
487
+ gr.Markdown("""
488
+ **Méthode de prédiction :**
489
+ 1. Calcul de l'IFT historique par parcelle et année
490
+ 2. Régression linéaire : IFT = pente × année + ordonnée_origine
491
+ 3. Extrapolation aux années 2025-2027
492
+ 4. Classification des risques :
493
+ - 🟢 Faible : IFT < 1.0
494
+ - 🟡 Modéré : 1.0 ≤ IFT < 2.0
495
+ - 🔴 Élevé : IFT ≥ 2.0
496
+ """)
497
+
498
  predict_btn = gr.Button("🎯 Prédire 2025-2027", variant="primary")
499
 
500
  with gr.Row():
 
504
  predict_btn.click(predict_future_weed_pressure, outputs=[predictions_plot, predictions_summary])
505
 
506
  with gr.Tab("🌱 Recommandations"):
507
+ gr.Markdown("### Recommandations pour cultures sensibles (pois, haricot)")
508
+ gr.Markdown("""
509
+ **Méthode de recommandation :**
510
+ 1. Prédiction IFT 2025-2027 par régression linéaire
511
+ 2. Filtrage des parcelles à faible risque (IFT < 1.0)
512
+ 3. Calcul du score de recommandation : 100 - (IFT_prédit × 30)
513
+ 4. Classement par score (plus élevé = meilleur)
514
+ """)
515
+
516
  recommend_btn = gr.Button("🎯 Recommander Parcelles", variant="primary")
517
 
518
  with gr.Row():
 
521
 
522
  recommend_btn.click(recommend_sensitive_crop_plots, outputs=[recommendations_plot, recommendations_summary])
523
 
524
+ with gr.Tab("📊 Exploration Données"):
525
+ gr.Markdown("### Explorer les données brutes de la Station Expérimentale de Kerguéhennec")
526
+
527
+ with gr.Row():
528
+ with gr.Column():
529
+ data_year_start = gr.Slider(
530
+ minimum=2014,
531
+ maximum=2025,
532
+ value=2020,
533
+ step=1,
534
+ label="Année de début"
535
+ )
536
+ data_year_end = gr.Slider(
537
+ minimum=2014,
538
+ maximum=2025,
539
+ value=2025,
540
+ step=1,
541
+ label="Année de fin"
542
+ )
543
+ data_plot_filter = gr.Dropdown(
544
+ choices=get_available_plots(),
545
+ value="Toutes",
546
+ label="Filtrer par parcelle"
547
+ )
548
+ data_crop_filter = gr.Dropdown(
549
+ choices=get_available_crops(),
550
+ value="Toutes",
551
+ label="Filtrer par culture"
552
+ )
553
+ data_intervention_filter = gr.Dropdown(
554
+ choices=get_available_interventions(),
555
+ value="Toutes",
556
+ label="Filtrer par type d'intervention"
557
+ )
558
+ explore_btn = gr.Button("🔍 Explorer les Données", variant="primary")
559
+
560
+ with gr.Row():
561
+ data_plot = gr.Plot()
562
+ data_summary = gr.Markdown()
563
 
564
+ explore_btn.click(
565
+ explore_raw_data,
566
+ inputs=[data_year_start, data_year_end, data_plot_filter, data_crop_filter, data_intervention_filter],
567
+ outputs=[data_plot, data_summary]
568
+ )
569
 
570
  return demo
571
 
test_improved_interface.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test de l'interface améliorée
3
+ """
4
+
5
+ from mcp_server import analyze_herbicide_trends, predict_future_weed_pressure, recommend_sensitive_crop_plots, explore_raw_data
6
+
7
+ def test_improved_functions():
8
+ """Test des fonctions améliorées"""
9
+ print("🧪 Test de l'interface améliorée...")
10
+
11
+ # Test 1: Analyse tendances
12
+ print("\nTest 1: Analyse tendances")
13
+ fig, summary = analyze_herbicide_trends(2020, 2024, "Toutes")
14
+ if fig is not None:
15
+ print("✅ Analyse tendances fonctionne")
16
+ else:
17
+ print("❌ Erreur:", summary)
18
+
19
+ # Test 2: Prédictions
20
+ print("\nTest 2: Prédictions")
21
+ fig, summary = predict_future_weed_pressure()
22
+ if fig is not None:
23
+ print("✅ Prédictions fonctionnent")
24
+ else:
25
+ print("❌ Erreur:", summary)
26
+
27
+ # Test 3: Recommandations
28
+ print("\nTest 3: Recommandations")
29
+ fig, summary = recommend_sensitive_crop_plots()
30
+ if fig is not None:
31
+ print("✅ Recommandations fonctionnent")
32
+ else:
33
+ print("❌ Erreur:", summary)
34
+
35
+ # Test 4: Exploration données
36
+ print("\nTest 4: Exploration données")
37
+ fig, summary = explore_raw_data(2020, 2024, "Toutes", "Toutes", "Toutes")
38
+ if fig is not None:
39
+ print("✅ Exploration données fonctionne")
40
+ else:
41
+ print("❌ Erreur:", summary)
42
+
43
+ if __name__ == "__main__":
44
+ test_improved_functions()