Spaces:
Sleeping
Sleeping
Tracy André
commited on
Commit
·
86d3300
1
Parent(s):
3537198
updated
Browse files- .gitattributes +0 -35
- .gitignore +0 -14
- DEPLOYMENT.md +0 -78
- GUIDE_UTILISATEUR.md +0 -138
- README.md +0 -31
- app.py +0 -303
- config.py +0 -32
- data_loader.py +0 -142
- example_agent_usage.py +0 -151
- herbicide_analyzer.py +0 -187
- requirements.txt +0 -11
- run_server.sh +0 -30
- test_mcp_server.py +0 -94
.gitattributes
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
__pycache__/
|
| 2 |
-
*.pyc
|
| 3 |
-
*.pyo
|
| 4 |
-
*.pyd
|
| 5 |
-
.Python
|
| 6 |
-
*.so
|
| 7 |
-
.coverage
|
| 8 |
-
.pytest_cache/
|
| 9 |
-
.env
|
| 10 |
-
.venv
|
| 11 |
-
env/
|
| 12 |
-
venv/
|
| 13 |
-
flagged/
|
| 14 |
-
*.log
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEPLOYMENT.md
DELETED
|
@@ -1,78 +0,0 @@
|
|
| 1 |
-
# Déploiement du Serveur MCP Agriculture sur Hugging Face
|
| 2 |
-
|
| 3 |
-
## Aperçu
|
| 4 |
-
Ce serveur MCP (Model Context Protocol) expose tous les outils d'analyse agricole et de visualisation via une interface Gradio optimisée pour Hugging Face Spaces.
|
| 5 |
-
|
| 6 |
-
## Architecture
|
| 7 |
-
- **app.py** : Serveur MCP principal avec interface Gradio
|
| 8 |
-
- **data_loader.py** : Chargement des données depuis Hugging Face
|
| 9 |
-
- **herbicide_analyzer.py** : Outils d'analyse des herbicides
|
| 10 |
-
- **config.py** : Configuration centralisée
|
| 11 |
-
|
| 12 |
-
## Fonctionnalités MCP
|
| 13 |
-
|
| 14 |
-
### 1. Chargement des données
|
| 15 |
-
- `load_agriculture_data()` : Charge les données depuis le dataset HackathonCRA/2024
|
| 16 |
-
- `get_data_summary()` : Résumé des données chargées
|
| 17 |
-
|
| 18 |
-
### 2. Analyse des herbicides
|
| 19 |
-
- `analyze_herbicide_usage()` : Analyse statistique des herbicides
|
| 20 |
-
- Types d'analyse : "statistics", "top_parcels", "trends"
|
| 21 |
-
|
| 22 |
-
### 3. Visualisations
|
| 23 |
-
- `create_herbicide_visualization()` : Graphiques Plotly interactifs
|
| 24 |
-
- Types : "top_ift", "regional_trends"
|
| 25 |
-
|
| 26 |
-
### 4. Requêtes base de données
|
| 27 |
-
- `query_database()` : Requêtes flexibles sur les données
|
| 28 |
-
- Types : "parcels_by_commune", "products_by_year"
|
| 29 |
-
|
| 30 |
-
## Déploiement sur Hugging Face
|
| 31 |
-
|
| 32 |
-
### 1. Créer un Space
|
| 33 |
-
```bash
|
| 34 |
-
git clone https://huggingface.co/spaces/YOUR_USERNAME/agriculture-mcp
|
| 35 |
-
cd agriculture-mcp
|
| 36 |
-
```
|
| 37 |
-
|
| 38 |
-
### 2. Copier les fichiers
|
| 39 |
-
```bash
|
| 40 |
-
cp -r /path/to/mcp/* .
|
| 41 |
-
```
|
| 42 |
-
|
| 43 |
-
### 3. Configurer les variables d'environnement
|
| 44 |
-
Dans les settings du Space Hugging Face :
|
| 45 |
-
- `HF_TOKEN` : Token Hugging Face pour accéder au dataset
|
| 46 |
-
|
| 47 |
-
### 4. Push et déploiement
|
| 48 |
-
```bash
|
| 49 |
-
git add .
|
| 50 |
-
git commit -m "Initial MCP server deployment"
|
| 51 |
-
git push
|
| 52 |
-
```
|
| 53 |
-
|
| 54 |
-
## Utilisation avec un Agent IA
|
| 55 |
-
|
| 56 |
-
L'agent IA peut se connecter au serveur MCP et utiliser les outils exposés :
|
| 57 |
-
|
| 58 |
-
```python
|
| 59 |
-
# Exemple d'utilisation par un agent IA
|
| 60 |
-
agent.connect_mcp_server("https://your-space.hf.space")
|
| 61 |
-
|
| 62 |
-
# Charger les données
|
| 63 |
-
agent.call_tool("load_agriculture_data")
|
| 64 |
-
|
| 65 |
-
# Analyser les herbicides
|
| 66 |
-
agent.call_tool("analyze_herbicide_usage", year=2023, analysis_type="statistics")
|
| 67 |
-
|
| 68 |
-
# Créer une visualisation
|
| 69 |
-
agent.call_tool("create_herbicide_visualization", year=2023, chart_type="top_ift")
|
| 70 |
-
```
|
| 71 |
-
|
| 72 |
-
## Avantages
|
| 73 |
-
|
| 74 |
-
1. **Interface unifiée** : Tous les outils dans un seul serveur MCP
|
| 75 |
-
2. **Visualisations interactives** : Graphiques Plotly embarqués
|
| 76 |
-
3. **Déploiement simple** : Prêt pour Hugging Face Spaces
|
| 77 |
-
4. **Extensible** : Facile d'ajouter de nouveaux outils
|
| 78 |
-
5. **Performance** : Optimisé pour les requêtes d'agents IA
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GUIDE_UTILISATEUR.md
DELETED
|
@@ -1,138 +0,0 @@
|
|
| 1 |
-
# 🌾 Guide Utilisateur - Serveur MCP Agriculture
|
| 2 |
-
|
| 3 |
-
## Vue d'ensemble
|
| 4 |
-
|
| 5 |
-
Le serveur MCP (Model Context Protocol) Agriculture expose tous les outils d'analyse agricole et de visualisation via une interface Gradio. Il permet aux agents IA de se connecter aux données agricoles pour générer des rapports puissants.
|
| 6 |
-
|
| 7 |
-
## 🚀 Démarrage Rapide
|
| 8 |
-
|
| 9 |
-
### 1. Installation locale
|
| 10 |
-
```bash
|
| 11 |
-
cd mcp/
|
| 12 |
-
pip install -r requirements.txt
|
| 13 |
-
python app.py
|
| 14 |
-
```
|
| 15 |
-
|
| 16 |
-
### 2. Interface web
|
| 17 |
-
Ouvrez http://localhost:7860 dans votre navigateur
|
| 18 |
-
|
| 19 |
-
### 3. Test du serveur
|
| 20 |
-
```bash
|
| 21 |
-
python test_mcp_server.py
|
| 22 |
-
```
|
| 23 |
-
|
| 24 |
-
## 🛠️ Outils MCP Disponibles
|
| 25 |
-
|
| 26 |
-
### 📊 Gestion des données
|
| 27 |
-
- **`load_agriculture_data()`** : Charge les données depuis Hugging Face
|
| 28 |
-
- **`get_data_summary()`** : Obtient un résumé des données chargées
|
| 29 |
-
|
| 30 |
-
### 🌿 Analyse des herbicides
|
| 31 |
-
- **`analyze_herbicide_usage(year, analysis_type)`** : Analyse l'usage d'herbicides
|
| 32 |
-
- `analysis_type` : "statistics", "top_parcels", "trends"
|
| 33 |
-
|
| 34 |
-
### 📈 Visualisations
|
| 35 |
-
- **`create_herbicide_visualization(year, chart_type)`** : Crée des graphiques
|
| 36 |
-
- `chart_type` : "top_ift", "regional_trends"
|
| 37 |
-
|
| 38 |
-
### 🔍 Requêtes base de données
|
| 39 |
-
- **`query_database(query_type, **kwargs)`** : Requêtes flexibles
|
| 40 |
-
- `query_type` : "parcels_by_commune", "products_by_year"
|
| 41 |
-
|
| 42 |
-
## 🤖 Utilisation par un Agent IA
|
| 43 |
-
|
| 44 |
-
### Exemple de workflow complet :
|
| 45 |
-
|
| 46 |
-
```python
|
| 47 |
-
# 1. Connexion au serveur MCP
|
| 48 |
-
agent.connect_mcp_server("https://your-space.hf.space")
|
| 49 |
-
|
| 50 |
-
# 2. Chargement des données
|
| 51 |
-
result = agent.call_tool("load_agriculture_data")
|
| 52 |
-
|
| 53 |
-
# 3. Analyse des herbicides
|
| 54 |
-
analysis = agent.call_tool("analyze_herbicide_usage",
|
| 55 |
-
year=2023,
|
| 56 |
-
analysis_type="statistics")
|
| 57 |
-
|
| 58 |
-
# 4. Création de visualisation
|
| 59 |
-
chart = agent.call_tool("create_herbicide_visualization",
|
| 60 |
-
year=2023,
|
| 61 |
-
chart_type="top_ift")
|
| 62 |
-
|
| 63 |
-
# 5. Génération de rapport
|
| 64 |
-
report = agent.generate_report(analysis, chart)
|
| 65 |
-
```
|
| 66 |
-
|
| 67 |
-
## 📋 Interface Gradio
|
| 68 |
-
|
| 69 |
-
L'interface web contient 4 onglets :
|
| 70 |
-
|
| 71 |
-
1. **📊 Chargement des Données** : Charger et résumer les données
|
| 72 |
-
2. **🌿 Analyse des Herbicides** : Analyses spécialisées herbicides
|
| 73 |
-
3. **📈 Visualisations** : Création de graphiques interactifs
|
| 74 |
-
4. **🔍 Requêtes Base de Données** : Requêtes personnalisées
|
| 75 |
-
|
| 76 |
-
## 🎯 Cas d'usage
|
| 77 |
-
|
| 78 |
-
### 1. Analyse de compliance
|
| 79 |
-
```python
|
| 80 |
-
# Identifier les parcelles à risque
|
| 81 |
-
top_parcels = agent.call_tool("analyze_herbicide_usage",
|
| 82 |
-
year=2023,
|
| 83 |
-
analysis_type="top_parcels")
|
| 84 |
-
```
|
| 85 |
-
|
| 86 |
-
### 2. Suivi de tendances
|
| 87 |
-
```python
|
| 88 |
-
# Analyser l'évolution par région
|
| 89 |
-
trends = agent.call_tool("analyze_herbicide_usage",
|
| 90 |
-
analysis_type="trends")
|
| 91 |
-
```
|
| 92 |
-
|
| 93 |
-
### 3. Reporting automatisé
|
| 94 |
-
```python
|
| 95 |
-
# Générer des rapports périodiques
|
| 96 |
-
for year in [2021, 2022, 2023]:
|
| 97 |
-
analysis = agent.call_tool("analyze_herbicide_usage", year=year)
|
| 98 |
-
chart = agent.call_tool("create_herbicide_visualization", year=year)
|
| 99 |
-
agent.generate_report(f"Rapport {year}", analysis, chart)
|
| 100 |
-
```
|
| 101 |
-
|
| 102 |
-
## 🔧 Configuration
|
| 103 |
-
|
| 104 |
-
### Variables d'environnement
|
| 105 |
-
- `HF_TOKEN` : Token Hugging Face pour accéder aux datasets
|
| 106 |
-
- `GRADIO_ANALYTICS_ENABLED=False` : Désactive les analytics
|
| 107 |
-
|
| 108 |
-
### Personnalisation
|
| 109 |
-
- **config.py** : Configuration centrale (couleurs, messages, etc.)
|
| 110 |
-
- **requirements.txt** : Dépendances Python
|
| 111 |
-
- **DEPLOYMENT.md** : Guide de déploiement détaillé
|
| 112 |
-
|
| 113 |
-
## 🌐 Déploiement sur Hugging Face
|
| 114 |
-
|
| 115 |
-
1. Créer un nouveau Space sur Hugging Face
|
| 116 |
-
2. Copier tous les fichiers du dossier `mcp/`
|
| 117 |
-
3. Configurer la variable `HF_TOKEN` dans les settings
|
| 118 |
-
4. Le déploiement se fait automatiquement
|
| 119 |
-
|
| 120 |
-
## 🔄 Extensibilité
|
| 121 |
-
|
| 122 |
-
Pour ajouter de nouveaux outils MCP :
|
| 123 |
-
|
| 124 |
-
1. Ajouter la méthode dans `AgricultureMCPServer`
|
| 125 |
-
2. Exposer via l'interface Gradio
|
| 126 |
-
3. Documenter dans ce guide
|
| 127 |
-
4. Tester avec `test_mcp_server.py`
|
| 128 |
-
|
| 129 |
-
## 🆘 Support
|
| 130 |
-
|
| 131 |
-
- **Tests** : `python test_mcp_server.py`
|
| 132 |
-
- **Exemple** : `python example_agent_usage.py`
|
| 133 |
-
- **Logs** : Vérifier la console pour les erreurs
|
| 134 |
-
- **Documentation** : Voir DEPLOYMENT.md pour plus de détails
|
| 135 |
-
|
| 136 |
-
---
|
| 137 |
-
|
| 138 |
-
🎉 **Le serveur MCP Agriculture est prêt à connecter les agents IA aux données agricoles !**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Agriculture Data MCP Server
|
| 3 |
-
emoji: 🌾
|
| 4 |
-
colorFrom: green
|
| 5 |
-
colorTo: blue
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 5.46.0
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
-
---
|
| 11 |
-
|
| 12 |
-
# Agriculture Data MCP Server
|
| 13 |
-
|
| 14 |
-
Ce serveur MCP (Model Context Protocol) expose tous les outils d'analyse agricole et de visualisation de données via une interface Gradio. Il permet aux agents IA de se connecter aux données agricoles et de générer des rapports puissants.
|
| 15 |
-
|
| 16 |
-
## Fonctionnalités
|
| 17 |
-
|
| 18 |
-
- **Analyse des herbicides** : Outils spécialisés pour analyser l'utilisation d'herbicides
|
| 19 |
-
- **Visualisations** : Graphiques interactifs avec Plotly
|
| 20 |
-
- **Base de données** : Requêtes sur les données agricoles Hugging Face
|
| 21 |
-
- **Agent IA** : Interface pour connecter des LLM aux données et graphiques
|
| 22 |
-
|
| 23 |
-
## Utilisation
|
| 24 |
-
|
| 25 |
-
1. L'interface Gradio expose plusieurs outils MCP
|
| 26 |
-
2. Chaque outil peut être appelé par un agent IA
|
| 27 |
-
3. Les résultats incluent des visualisations et des analyses de données
|
| 28 |
-
|
| 29 |
-
## Déploiement
|
| 30 |
-
|
| 31 |
-
Ce serveur est conçu pour être déployé sur Hugging Face Spaces avec le support MCP intégré.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
DELETED
|
@@ -1,303 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Serveur MCP pour l'analyse des données agricoles
|
| 3 |
-
Expose les outils d'analyse et de visualisation via Gradio avec support MCP
|
| 4 |
-
"""
|
| 5 |
-
import os
|
| 6 |
-
import json
|
| 7 |
-
import traceback
|
| 8 |
-
import pandas as pd
|
| 9 |
-
import gradio as gr
|
| 10 |
-
import plotly.express as px
|
| 11 |
-
import plotly.graph_objects as go
|
| 12 |
-
from plotly.subplots import make_subplots
|
| 13 |
-
|
| 14 |
-
# Configuration pour désactiver les analytics Gradio
|
| 15 |
-
os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
|
| 16 |
-
|
| 17 |
-
# Import des modules locaux
|
| 18 |
-
from data_loader import DataLoader
|
| 19 |
-
from herbicide_analyzer import HerbicideAnalyzer
|
| 20 |
-
from config import DATASET_ID, HF_TOKEN, PLOT_CONFIG
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
class AgricultureMCPServer:
|
| 24 |
-
"""Serveur MCP pour l'analyse des données agricoles"""
|
| 25 |
-
|
| 26 |
-
def __init__(self):
|
| 27 |
-
self.data_loader = DataLoader()
|
| 28 |
-
self.herbicide_analyzer = HerbicideAnalyzer()
|
| 29 |
-
self.df = None
|
| 30 |
-
self.is_data_loaded = False
|
| 31 |
-
|
| 32 |
-
def load_agriculture_data(self) -> str:
|
| 33 |
-
"""
|
| 34 |
-
Charge les données agricoles depuis Hugging Face
|
| 35 |
-
|
| 36 |
-
Returns:
|
| 37 |
-
str: Message de statut du chargement
|
| 38 |
-
"""
|
| 39 |
-
try:
|
| 40 |
-
status = self.data_loader.load_data()
|
| 41 |
-
self.df = self.data_loader.get_data()
|
| 42 |
-
|
| 43 |
-
if self.df is not None:
|
| 44 |
-
self.herbicide_analyzer.set_data(self.df)
|
| 45 |
-
self.is_data_loaded = True
|
| 46 |
-
return f"✅ Données chargées: {len(self.df)} lignes, {len(self.df.columns)} colonnes"
|
| 47 |
-
else:
|
| 48 |
-
self.is_data_loaded = False
|
| 49 |
-
return f"❌ Échec du chargement: {status}"
|
| 50 |
-
|
| 51 |
-
except Exception as e:
|
| 52 |
-
self.is_data_loaded = False
|
| 53 |
-
return f"❌ Erreur lors du chargement: {str(e)}"
|
| 54 |
-
|
| 55 |
-
def get_data_summary(self) -> str:
|
| 56 |
-
"""
|
| 57 |
-
Obtient un résumé des données chargées
|
| 58 |
-
|
| 59 |
-
Returns:
|
| 60 |
-
str: JSON avec les statistiques des données
|
| 61 |
-
"""
|
| 62 |
-
try:
|
| 63 |
-
if not self.is_data_loaded or self.df is None:
|
| 64 |
-
return json.dumps({"error": "Aucune donnée chargée. Utilisez load_agriculture_data() d'abord."})
|
| 65 |
-
|
| 66 |
-
summary = {
|
| 67 |
-
"nombre_lignes": len(self.df),
|
| 68 |
-
"nombre_colonnes": len(self.df.columns),
|
| 69 |
-
"colonnes": list(self.df.columns),
|
| 70 |
-
"annees_disponibles": sorted(self.df['millesime'].unique().tolist()) if 'millesime' in self.df.columns else [],
|
| 71 |
-
"nombre_parcelles": self.df['numparcell'].nunique() if 'numparcell' in self.df.columns else 0,
|
| 72 |
-
"familles_produits": self.df['familleprod'].unique().tolist() if 'familleprod' in self.df.columns else []
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
return json.dumps(summary, indent=2, ensure_ascii=False)
|
| 76 |
-
|
| 77 |
-
except Exception as e:
|
| 78 |
-
return json.dumps({"error": f"Erreur lors du résumé: {str(e)}"})
|
| 79 |
-
|
| 80 |
-
def analyze_herbicide_usage(self, year: int = 2023, analysis_type: str = "statistics") -> str:
|
| 81 |
-
"""
|
| 82 |
-
Analyse l'utilisation des herbicides
|
| 83 |
-
|
| 84 |
-
Args:
|
| 85 |
-
year (int): Année à analyser
|
| 86 |
-
analysis_type (str): Type d'analyse ("statistics", "top_parcels", "trends")
|
| 87 |
-
|
| 88 |
-
Returns:
|
| 89 |
-
str: JSON avec les résultats de l'analyse
|
| 90 |
-
"""
|
| 91 |
-
try:
|
| 92 |
-
if not self.is_data_loaded:
|
| 93 |
-
return json.dumps({"error": "Données non chargées. Utilisez load_agriculture_data() d'abord."})
|
| 94 |
-
|
| 95 |
-
if analysis_type == "statistics":
|
| 96 |
-
stats, msg = self.herbicide_analyzer.get_herbicide_usage_statistics(year)
|
| 97 |
-
if stats:
|
| 98 |
-
return json.dumps({"status": msg, "data": stats}, indent=2, ensure_ascii=False)
|
| 99 |
-
else:
|
| 100 |
-
return json.dumps({"error": msg})
|
| 101 |
-
|
| 102 |
-
elif analysis_type == "top_parcels":
|
| 103 |
-
parcels, msg = self.herbicide_analyzer.get_top_ift_parcels_by_year(year, 10)
|
| 104 |
-
if parcels is not None:
|
| 105 |
-
# Convertir DataFrame en dict pour JSON
|
| 106 |
-
return json.dumps({
|
| 107 |
-
"status": msg,
|
| 108 |
-
"data": parcels.to_dict('records')
|
| 109 |
-
}, indent=2, ensure_ascii=False)
|
| 110 |
-
else:
|
| 111 |
-
return json.dumps({"error": msg})
|
| 112 |
-
|
| 113 |
-
elif analysis_type == "trends":
|
| 114 |
-
trends, msg = self.herbicide_analyzer.analyze_herbicide_trends_by_region()
|
| 115 |
-
if trends is not None:
|
| 116 |
-
return json.dumps({
|
| 117 |
-
"status": msg,
|
| 118 |
-
"data": trends.head(20).to_dict('records') # Limiter pour éviter des réponses trop longues
|
| 119 |
-
}, indent=2, ensure_ascii=False)
|
| 120 |
-
else:
|
| 121 |
-
return json.dumps({"error": msg})
|
| 122 |
-
|
| 123 |
-
else:
|
| 124 |
-
return json.dumps({"error": f"Type d'analyse '{analysis_type}' non supporté"})
|
| 125 |
-
|
| 126 |
-
except Exception as e:
|
| 127 |
-
return json.dumps({"error": f"Erreur lors de l'analyse: {str(e)}"})
|
| 128 |
-
|
| 129 |
-
def create_herbicide_visualization(self, year: int = 2023, chart_type: str = "top_ift") -> go.Figure:
|
| 130 |
-
"""
|
| 131 |
-
Crée une visualisation des données herbicides
|
| 132 |
-
|
| 133 |
-
Args:
|
| 134 |
-
year (int): Année à analyser
|
| 135 |
-
chart_type (str): Type de graphique ("top_ift", "regional_trends")
|
| 136 |
-
|
| 137 |
-
Returns:
|
| 138 |
-
plotly.graph_objects.Figure: Graphique Plotly
|
| 139 |
-
"""
|
| 140 |
-
try:
|
| 141 |
-
if not self.is_data_loaded:
|
| 142 |
-
return self._create_error_plot("Données non chargées", "Utilisez load_agriculture_data() d'abord")
|
| 143 |
-
|
| 144 |
-
return self.herbicide_analyzer.create_herbicide_visualization(
|
| 145 |
-
analysis_type=chart_type,
|
| 146 |
-
year=year,
|
| 147 |
-
n_parcels=10
|
| 148 |
-
)
|
| 149 |
-
|
| 150 |
-
except Exception as e:
|
| 151 |
-
return self._create_error_plot("Erreur de visualisation", f"Erreur: {str(e)}")
|
| 152 |
-
|
| 153 |
-
def query_database(self, query_type: str, **kwargs) -> str:
|
| 154 |
-
"""
|
| 155 |
-
Effectue une requête sur la base de données
|
| 156 |
-
|
| 157 |
-
Args:
|
| 158 |
-
query_type (str): Type de requête
|
| 159 |
-
**kwargs: Paramètres de la requête
|
| 160 |
-
|
| 161 |
-
Returns:
|
| 162 |
-
str: JSON avec les résultats
|
| 163 |
-
"""
|
| 164 |
-
try:
|
| 165 |
-
if not self.is_data_loaded:
|
| 166 |
-
return json.dumps({"error": "Données non chargées"})
|
| 167 |
-
|
| 168 |
-
if query_type == "parcels_by_commune":
|
| 169 |
-
commune = kwargs.get('commune', '')
|
| 170 |
-
if 'nomcommune' in self.df.columns:
|
| 171 |
-
results = self.df[self.df['nomcommune'].str.contains(commune, case=False, na=False)]
|
| 172 |
-
return json.dumps({
|
| 173 |
-
"count": len(results),
|
| 174 |
-
"data": results.head(50).to_dict('records')
|
| 175 |
-
}, ensure_ascii=False)
|
| 176 |
-
else:
|
| 177 |
-
return json.dumps({"error": "Colonne 'nomcommune' non disponible"})
|
| 178 |
-
|
| 179 |
-
elif query_type == "products_by_year":
|
| 180 |
-
year = kwargs.get('year', 2023)
|
| 181 |
-
year_data = self.df[self.df['millesime'] == year]
|
| 182 |
-
products = year_data['familleprod'].value_counts().to_dict() if 'familleprod' in year_data.columns else {}
|
| 183 |
-
return json.dumps({
|
| 184 |
-
"year": year,
|
| 185 |
-
"products": products
|
| 186 |
-
}, ensure_ascii=False)
|
| 187 |
-
|
| 188 |
-
else:
|
| 189 |
-
return json.dumps({"error": f"Type de requête '{query_type}' non supporté"})
|
| 190 |
-
|
| 191 |
-
except Exception as e:
|
| 192 |
-
return json.dumps({"error": f"Erreur de requête: {str(e)}"})
|
| 193 |
-
|
| 194 |
-
def _create_error_plot(self, title: str, message: str) -> go.Figure:
|
| 195 |
-
"""Crée un graphique d'erreur"""
|
| 196 |
-
fig = go.Figure()
|
| 197 |
-
fig.add_annotation(
|
| 198 |
-
text=message,
|
| 199 |
-
xref="paper", yref="paper",
|
| 200 |
-
x=0.5, y=0.5,
|
| 201 |
-
showarrow=False,
|
| 202 |
-
font=dict(size=14, color="red")
|
| 203 |
-
)
|
| 204 |
-
fig.update_layout(title=title, width=600, height=400)
|
| 205 |
-
return fig
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
# Initialisation du serveur MCP
|
| 209 |
-
mcp_server = AgricultureMCPServer()
|
| 210 |
-
|
| 211 |
-
# Interface Gradio avec support MCP
|
| 212 |
-
with gr.Blocks(title="Agriculture Data MCP Server", theme=gr.themes.Soft()) as demo:
|
| 213 |
-
gr.Markdown("""
|
| 214 |
-
# 🌾 Agriculture Data MCP Server
|
| 215 |
-
|
| 216 |
-
Ce serveur MCP expose des outils d'analyse des données agricoles via le Model Context Protocol.
|
| 217 |
-
Connectez un agent IA pour analyser les données et générer des rapports puissants.
|
| 218 |
-
""")
|
| 219 |
-
|
| 220 |
-
with gr.Tab("📊 Chargement des Données"):
|
| 221 |
-
load_btn = gr.Button("Charger les données depuis Hugging Face", variant="primary")
|
| 222 |
-
load_output = gr.Textbox(label="Statut du chargement", lines=3)
|
| 223 |
-
|
| 224 |
-
summary_btn = gr.Button("Obtenir le résumé des données")
|
| 225 |
-
summary_output = gr.JSON(label="Résumé des données")
|
| 226 |
-
|
| 227 |
-
with gr.Tab("🌿 Analyse des Herbicides"):
|
| 228 |
-
with gr.Row():
|
| 229 |
-
herb_year = gr.Number(value=2023, label="Année", precision=0)
|
| 230 |
-
herb_analysis = gr.Dropdown(
|
| 231 |
-
choices=["statistics", "top_parcels", "trends"],
|
| 232 |
-
value="statistics",
|
| 233 |
-
label="Type d'analyse"
|
| 234 |
-
)
|
| 235 |
-
|
| 236 |
-
herb_analyze_btn = gr.Button("Analyser les herbicides")
|
| 237 |
-
herb_output = gr.JSON(label="Résultats de l'analyse")
|
| 238 |
-
|
| 239 |
-
with gr.Tab("📈 Visualisations"):
|
| 240 |
-
with gr.Row():
|
| 241 |
-
viz_year = gr.Number(value=2023, label="Année", precision=0)
|
| 242 |
-
viz_type = gr.Dropdown(
|
| 243 |
-
choices=["top_ift", "regional_trends"],
|
| 244 |
-
value="top_ift",
|
| 245 |
-
label="Type de graphique"
|
| 246 |
-
)
|
| 247 |
-
|
| 248 |
-
viz_btn = gr.Button("Créer la visualisation")
|
| 249 |
-
viz_output = gr.Plot(label="Graphique")
|
| 250 |
-
|
| 251 |
-
with gr.Tab("🔍 Requêtes Base de Données"):
|
| 252 |
-
with gr.Row():
|
| 253 |
-
query_type = gr.Dropdown(
|
| 254 |
-
choices=["parcels_by_commune", "products_by_year"],
|
| 255 |
-
value="parcels_by_commune",
|
| 256 |
-
label="Type de requête"
|
| 257 |
-
)
|
| 258 |
-
query_param = gr.Textbox(label="Paramètre (commune ou année)", value="")
|
| 259 |
-
|
| 260 |
-
query_btn = gr.Button("Exécuter la requête")
|
| 261 |
-
query_output = gr.JSON(label="Résultats de la requête")
|
| 262 |
-
|
| 263 |
-
# Connexions des événements
|
| 264 |
-
load_btn.click(
|
| 265 |
-
fn=mcp_server.load_agriculture_data,
|
| 266 |
-
outputs=load_output
|
| 267 |
-
)
|
| 268 |
-
|
| 269 |
-
summary_btn.click(
|
| 270 |
-
fn=lambda: json.loads(mcp_server.get_data_summary()),
|
| 271 |
-
outputs=summary_output
|
| 272 |
-
)
|
| 273 |
-
|
| 274 |
-
herb_analyze_btn.click(
|
| 275 |
-
fn=lambda year, analysis: json.loads(mcp_server.analyze_herbicide_usage(int(year), analysis)),
|
| 276 |
-
inputs=[herb_year, herb_analysis],
|
| 277 |
-
outputs=herb_output
|
| 278 |
-
)
|
| 279 |
-
|
| 280 |
-
viz_btn.click(
|
| 281 |
-
fn=mcp_server.create_herbicide_visualization,
|
| 282 |
-
inputs=[viz_year, viz_type],
|
| 283 |
-
outputs=viz_output
|
| 284 |
-
)
|
| 285 |
-
|
| 286 |
-
query_btn.click(
|
| 287 |
-
fn=lambda qtype, param: json.loads(mcp_server.query_database(
|
| 288 |
-
qtype,
|
| 289 |
-
commune=param if qtype == "parcels_by_commune" else None,
|
| 290 |
-
year=int(param) if qtype == "products_by_year" and param.isdigit() else 2023
|
| 291 |
-
)),
|
| 292 |
-
inputs=[query_type, query_param],
|
| 293 |
-
outputs=query_output
|
| 294 |
-
)
|
| 295 |
-
|
| 296 |
-
# Lancement du serveur avec support MCP
|
| 297 |
-
if __name__ == "__main__":
|
| 298 |
-
demo.launch(
|
| 299 |
-
server_name="0.0.0.0",
|
| 300 |
-
server_port=7860,
|
| 301 |
-
share=True,
|
| 302 |
-
mcp_server=True # Active le support MCP
|
| 303 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Configuration pour le serveur MCP Agriculture
|
| 3 |
-
"""
|
| 4 |
-
import os
|
| 5 |
-
|
| 6 |
-
# Configuration Hugging Face
|
| 7 |
-
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 8 |
-
DATASET_ID = "HackathonCRA/2024"
|
| 9 |
-
|
| 10 |
-
# Colonnes requises dans le dataset
|
| 11 |
-
REQUIRED_COLUMNS = ["numparcell", "surfparc", "millesime"]
|
| 12 |
-
|
| 13 |
-
# Configuration des couleurs pour les graphiques
|
| 14 |
-
RISK_COLORS = {
|
| 15 |
-
'high': '#FF4444',
|
| 16 |
-
'medium': '#FFA500',
|
| 17 |
-
'low': '#44AA44'
|
| 18 |
-
}
|
| 19 |
-
|
| 20 |
-
# Configuration des graphiques
|
| 21 |
-
PLOT_CONFIG = {
|
| 22 |
-
"width": 700,
|
| 23 |
-
"height": 400,
|
| 24 |
-
"template": "plotly_white"
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
# Messages constants
|
| 28 |
-
MESSAGES = {
|
| 29 |
-
"loading": "🔄 Chargement des données depuis Hugging Face...",
|
| 30 |
-
"no_data": "❌ Impossible de charger les données",
|
| 31 |
-
"success": "✅ Données chargées avec succès"
|
| 32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data_loader.py
DELETED
|
@@ -1,142 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Module de chargement des données depuis Hugging Face pour MCP
|
| 3 |
-
"""
|
| 4 |
-
import os
|
| 5 |
-
import traceback
|
| 6 |
-
import pandas as pd
|
| 7 |
-
from datasets import load_dataset
|
| 8 |
-
from huggingface_hub import HfApi, hf_hub_download
|
| 9 |
-
from config import HF_TOKEN, DATASET_ID, REQUIRED_COLUMNS, MESSAGES
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class DataLoader:
|
| 13 |
-
"""Classe responsable du chargement des données depuis différentes sources"""
|
| 14 |
-
|
| 15 |
-
def __init__(self):
|
| 16 |
-
self.df = None
|
| 17 |
-
|
| 18 |
-
def load_data(self):
|
| 19 |
-
"""Charge les données du dataset avec gestion robuste des erreurs."""
|
| 20 |
-
try:
|
| 21 |
-
print(MESSAGES["loading"])
|
| 22 |
-
print(f"📋 Dataset ID: {DATASET_ID}")
|
| 23 |
-
print(f"📋 Token disponible: {'Oui' if HF_TOKEN else 'Non'}")
|
| 24 |
-
|
| 25 |
-
self.df = None
|
| 26 |
-
|
| 27 |
-
# Stratégie 1: Chargement direct Hugging Face
|
| 28 |
-
print("🔄 Stratégie 1: chargement via datasets HF")
|
| 29 |
-
hf_msg = self._safe_load_from_hf()
|
| 30 |
-
if self.df is None:
|
| 31 |
-
if hf_msg:
|
| 32 |
-
print(f"❌ Chargement HF échoué: {hf_msg}")
|
| 33 |
-
|
| 34 |
-
# Stratégie 2: Charger directement les fichiers du repo
|
| 35 |
-
print("🔄 Stratégie 2: chargement via fichiers du dépôt")
|
| 36 |
-
fallback_msg = self._fallback_load_from_repo_files()
|
| 37 |
-
if self.df is None:
|
| 38 |
-
if fallback_msg:
|
| 39 |
-
print(f"❌ Chargement via fichiers échoué: {fallback_msg}")
|
| 40 |
-
return MESSAGES["no_data"]
|
| 41 |
-
|
| 42 |
-
if self.df is None:
|
| 43 |
-
return MESSAGES["no_data"]
|
| 44 |
-
|
| 45 |
-
print(f"📊 Données chargées: {len(self.df)} lignes")
|
| 46 |
-
print(f"📊 Colonnes disponibles: {list(self.df.columns)}")
|
| 47 |
-
|
| 48 |
-
# Validation des colonnes
|
| 49 |
-
missing_cols = [col for col in REQUIRED_COLUMNS if col not in self.df.columns]
|
| 50 |
-
if missing_cols:
|
| 51 |
-
print(f"❌ Colonnes manquantes: {missing_cols}")
|
| 52 |
-
self.df = None
|
| 53 |
-
return f"❌ Colonnes manquantes: {missing_cols}"
|
| 54 |
-
|
| 55 |
-
# Nettoyage
|
| 56 |
-
initial_len = len(self.df)
|
| 57 |
-
self.df = self.df.dropna(subset=REQUIRED_COLUMNS)
|
| 58 |
-
print(f"📊 Après nettoyage: {len(self.df)} lignes (était {initial_len})")
|
| 59 |
-
|
| 60 |
-
return MESSAGES["success"]
|
| 61 |
-
|
| 62 |
-
except Exception as e:
|
| 63 |
-
print(f"❌ Erreur générale: {str(e)}")
|
| 64 |
-
print(f"❌ Traceback: {traceback.format_exc()}")
|
| 65 |
-
return f"❌ Erreur: {str(e)}"
|
| 66 |
-
|
| 67 |
-
def _safe_load_from_hf(self):
|
| 68 |
-
"""Chargement sécurisé depuis Hugging Face"""
|
| 69 |
-
try:
|
| 70 |
-
dataset = load_dataset(
|
| 71 |
-
DATASET_ID,
|
| 72 |
-
split="train",
|
| 73 |
-
token=HF_TOKEN,
|
| 74 |
-
trust_remote_code=True,
|
| 75 |
-
)
|
| 76 |
-
print(f"📊 Dataset chargé: {len(dataset)} exemples")
|
| 77 |
-
|
| 78 |
-
try:
|
| 79 |
-
self.df = dataset.to_pandas()
|
| 80 |
-
print("✅ Conversion to_pandas() réussie")
|
| 81 |
-
return None
|
| 82 |
-
except Exception as pandas_error:
|
| 83 |
-
print(f"❌ Erreur to_pandas(): {pandas_error}")
|
| 84 |
-
print("🔄 Tentative de conversion manuelle...")
|
| 85 |
-
data_list = []
|
| 86 |
-
for i, item in enumerate(dataset):
|
| 87 |
-
data_list.append(item)
|
| 88 |
-
if i < 5:
|
| 89 |
-
print(f"📋 Exemple {i}: {list(item.keys())}")
|
| 90 |
-
self.df = pd.DataFrame(data_list)
|
| 91 |
-
print(f"✅ Conversion manuelle réussie: {len(self.df)} lignes")
|
| 92 |
-
return None
|
| 93 |
-
except Exception as e:
|
| 94 |
-
return f"Erreur HF: {str(e)}"
|
| 95 |
-
|
| 96 |
-
def _fallback_load_from_repo_files(self):
|
| 97 |
-
"""Fallback pour charger les données en téléchargeant directement les fichiers du repo HF."""
|
| 98 |
-
try:
|
| 99 |
-
print("🔄 Tentative de chargement alternatif via fichiers du dépôt...")
|
| 100 |
-
api = HfApi(token=HF_TOKEN)
|
| 101 |
-
repo_files = api.list_repo_files(repo_id=DATASET_ID, repo_type="dataset")
|
| 102 |
-
|
| 103 |
-
# Chercher des fichiers de données
|
| 104 |
-
data_files = [f for f in repo_files if f.endswith(('.csv', '.parquet', '.tsv', '.json'))]
|
| 105 |
-
print(f"📁 Fichiers de données trouvés: {data_files}")
|
| 106 |
-
|
| 107 |
-
if not data_files:
|
| 108 |
-
return "Aucun fichier de données trouvé"
|
| 109 |
-
|
| 110 |
-
# Essayer de charger le premier fichier trouvé
|
| 111 |
-
for file_path in data_files:
|
| 112 |
-
try:
|
| 113 |
-
print(f"📥 Téléchargement de {file_path}...")
|
| 114 |
-
local_file = hf_hub_download(
|
| 115 |
-
repo_id=DATASET_ID,
|
| 116 |
-
filename=file_path,
|
| 117 |
-
repo_type="dataset",
|
| 118 |
-
token=HF_TOKEN
|
| 119 |
-
)
|
| 120 |
-
|
| 121 |
-
if file_path.endswith('.csv') or file_path.endswith('.tsv'):
|
| 122 |
-
sep = '\t' if file_path.endswith('.tsv') else ','
|
| 123 |
-
self.df = pd.read_csv(local_file, sep=sep)
|
| 124 |
-
elif file_path.endswith('.parquet'):
|
| 125 |
-
self.df = pd.read_parquet(local_file)
|
| 126 |
-
elif file_path.endswith('.json'):
|
| 127 |
-
self.df = pd.read_json(local_file)
|
| 128 |
-
|
| 129 |
-
if self.df is not None and len(self.df) > 0:
|
| 130 |
-
print(f"✅ Fichier {file_path} chargé avec succès: {len(self.df)} lignes")
|
| 131 |
-
return None
|
| 132 |
-
except Exception as file_error:
|
| 133 |
-
print(f"❌ Erreur avec {file_path}: {file_error}")
|
| 134 |
-
continue
|
| 135 |
-
|
| 136 |
-
return "Impossible de charger aucun fichier"
|
| 137 |
-
except Exception as e:
|
| 138 |
-
return f"Erreur fallback: {str(e)}"
|
| 139 |
-
|
| 140 |
-
def get_data(self):
|
| 141 |
-
"""Retourne les données chargées"""
|
| 142 |
-
return self.df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
example_agent_usage.py
DELETED
|
@@ -1,151 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Exemple d'utilisation du serveur MCP Agriculture par un agent IA
|
| 3 |
-
"""
|
| 4 |
-
import json
|
| 5 |
-
import requests
|
| 6 |
-
import time
|
| 7 |
-
from typing import Dict, Any
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
class AIAgentMCPClient:
|
| 11 |
-
"""Client MCP pour agent IA"""
|
| 12 |
-
|
| 13 |
-
def __init__(self, server_url: str = "http://localhost:7860"):
|
| 14 |
-
self.server_url = server_url.rstrip('/')
|
| 15 |
-
self.session = requests.Session()
|
| 16 |
-
|
| 17 |
-
def call_mcp_tool(self, tool_name: str, **kwargs) -> Dict[str, Any]:
|
| 18 |
-
"""
|
| 19 |
-
Appelle un outil MCP sur le serveur
|
| 20 |
-
|
| 21 |
-
Args:
|
| 22 |
-
tool_name (str): Nom de l'outil MCP
|
| 23 |
-
**kwargs: Paramètres de l'outil
|
| 24 |
-
|
| 25 |
-
Returns:
|
| 26 |
-
Dict: Résultat de l'outil
|
| 27 |
-
"""
|
| 28 |
-
try:
|
| 29 |
-
# Pour ce serveur Gradio, nous simulons l'appel MCP
|
| 30 |
-
# En production, ceci utiliserait le protocol MCP standard
|
| 31 |
-
|
| 32 |
-
if tool_name == "load_agriculture_data":
|
| 33 |
-
return {"status": "✅ Données chargées avec succès"}
|
| 34 |
-
|
| 35 |
-
elif tool_name == "get_data_summary":
|
| 36 |
-
return {
|
| 37 |
-
"nombre_lignes": 50000,
|
| 38 |
-
"nombre_colonnes": 15,
|
| 39 |
-
"annees_disponibles": [2020, 2021, 2022, 2023],
|
| 40 |
-
"familles_produits": ["Herbicides", "Fongicides", "Insecticides"]
|
| 41 |
-
}
|
| 42 |
-
|
| 43 |
-
elif tool_name == "analyze_herbicide_usage":
|
| 44 |
-
return {
|
| 45 |
-
"status": "✅ Analyse terminée",
|
| 46 |
-
"data": {
|
| 47 |
-
"nombre_parcelles": 12500,
|
| 48 |
-
"surface_totale": 45678.9,
|
| 49 |
-
"ift_moyen": 2.34,
|
| 50 |
-
"nombre_communes": 156
|
| 51 |
-
}
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
else:
|
| 55 |
-
return {"error": f"Outil '{tool_name}' non trouvé"}
|
| 56 |
-
|
| 57 |
-
except Exception as e:
|
| 58 |
-
return {"error": f"Erreur d'appel MCP: {str(e)}"}
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
def demonstrate_ai_agent_workflow():
|
| 62 |
-
"""Démontre un workflow typique d'agent IA avec le serveur MCP"""
|
| 63 |
-
|
| 64 |
-
print("🤖 Démonstration d'un Agent IA avec MCP Agriculture")
|
| 65 |
-
print("=" * 60)
|
| 66 |
-
|
| 67 |
-
# Initialiser le client MCP
|
| 68 |
-
agent = AIAgentMCPClient()
|
| 69 |
-
|
| 70 |
-
# Scénario: L'agent doit analyser l'utilisation d'herbicides en 2023
|
| 71 |
-
print("📋 Tâche: Analyser l'utilisation d'herbicides en 2023")
|
| 72 |
-
print()
|
| 73 |
-
|
| 74 |
-
# Étape 1: Charger les données
|
| 75 |
-
print("1️⃣ Chargement des données...")
|
| 76 |
-
result = agent.call_mcp_tool("load_agriculture_data")
|
| 77 |
-
print(f" {result.get('status', result)}")
|
| 78 |
-
print()
|
| 79 |
-
|
| 80 |
-
# Étape 2: Obtenir un aperçu des données
|
| 81 |
-
print("2️⃣ Obtention du résumé des données...")
|
| 82 |
-
summary = agent.call_mcp_tool("get_data_summary")
|
| 83 |
-
if "error" not in summary:
|
| 84 |
-
print(f" 📊 {summary['nombre_lignes']} lignes de données")
|
| 85 |
-
print(f" 📅 Années: {summary['annees_disponibles']}")
|
| 86 |
-
print(f" 🧪 Produits: {', '.join(summary['familles_produits'])}")
|
| 87 |
-
else:
|
| 88 |
-
print(f" ❌ {summary['error']}")
|
| 89 |
-
print()
|
| 90 |
-
|
| 91 |
-
# Étape 3: Analyser les herbicides
|
| 92 |
-
print("3️⃣ Analyse des herbicides pour 2023...")
|
| 93 |
-
analysis = agent.call_mcp_tool("analyze_herbicide_usage", year=2023, analysis_type="statistics")
|
| 94 |
-
if "error" not in analysis:
|
| 95 |
-
data = analysis.get('data', {})
|
| 96 |
-
print(f" 🌿 Parcelles analysées: {data.get('nombre_parcelles', 0):,}")
|
| 97 |
-
print(f" 📏 Surface totale: {data.get('surface_totale', 0):,.1f} ha")
|
| 98 |
-
print(f" 📊 IFT moyen: {data.get('ift_moyen', 0):.2f}")
|
| 99 |
-
print(f" 🏘️ Communes: {data.get('nombre_communes', 0)}")
|
| 100 |
-
else:
|
| 101 |
-
print(f" ❌ {analysis['error']}")
|
| 102 |
-
print()
|
| 103 |
-
|
| 104 |
-
# Étape 4: Génération du rapport
|
| 105 |
-
print("4️⃣ Génération du rapport...")
|
| 106 |
-
report = generate_ai_report(summary, analysis)
|
| 107 |
-
print(report)
|
| 108 |
-
print()
|
| 109 |
-
|
| 110 |
-
print("✅ Workflow d'agent IA terminé!")
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
def generate_ai_report(summary: Dict, analysis: Dict) -> str:
|
| 114 |
-
"""Génère un rapport IA basé sur les données analysées"""
|
| 115 |
-
|
| 116 |
-
if "error" in summary or "error" in analysis:
|
| 117 |
-
return "❌ Impossible de générer le rapport à cause d'erreurs dans les données"
|
| 118 |
-
|
| 119 |
-
data = analysis.get('data', {})
|
| 120 |
-
|
| 121 |
-
report = f"""
|
| 122 |
-
📊 RAPPORT D'ANALYSE - HERBICIDES 2023
|
| 123 |
-
{'='*50}
|
| 124 |
-
|
| 125 |
-
📈 DONNÉES GÉNÉRALES:
|
| 126 |
-
• Dataset: {summary.get('nombre_lignes', 0):,} lignes de données
|
| 127 |
-
• Couverture: {len(summary.get('annees_disponibles', []))} années
|
| 128 |
-
• Types de produits: {len(summary.get('familles_produits', []))}
|
| 129 |
-
|
| 130 |
-
🌿 ANALYSE HERBICIDES:
|
| 131 |
-
• Parcelles analysées: {data.get('nombre_parcelles', 0):,}
|
| 132 |
-
• Surface totale: {data.get('surface_totale', 0):,.1f} hectares
|
| 133 |
-
• IFT moyen: {data.get('ift_moyen', 0):.2f}
|
| 134 |
-
• Communes concernées: {data.get('nombre_communes', 0)}
|
| 135 |
-
|
| 136 |
-
💡 INSIGHTS:
|
| 137 |
-
• L'IFT moyen de {data.get('ift_moyen', 0):.2f} indique {'un usage modéré' if data.get('ift_moyen', 0) < 3 else 'un usage intensif'} d'herbicides
|
| 138 |
-
• La couverture de {data.get('nombre_communes', 0)} communes offre une bonne représentativité territoriale
|
| 139 |
-
• Les données couvrent {data.get('surface_totale', 0):,.0f} ha, soit un échantillon significatif
|
| 140 |
-
|
| 141 |
-
🎯 RECOMMANDATIONS:
|
| 142 |
-
1. Analyser les parcelles avec IFT élevé pour identifier les pratiques optimisables
|
| 143 |
-
2. Comparer les tendances entre régions pour partager les bonnes pratiques
|
| 144 |
-
3. Suivre l'évolution de l'IFT moyen sur plusieurs années
|
| 145 |
-
"""
|
| 146 |
-
|
| 147 |
-
return report
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
if __name__ == "__main__":
|
| 151 |
-
demonstrate_ai_agent_workflow()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
herbicide_analyzer.py
DELETED
|
@@ -1,187 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Module d'analyse avancée des herbicides pour MCP
|
| 3 |
-
"""
|
| 4 |
-
import pandas as pd
|
| 5 |
-
import plotly.express as px
|
| 6 |
-
import plotly.graph_objects as go
|
| 7 |
-
from plotly.subplots import make_subplots
|
| 8 |
-
import numpy as np
|
| 9 |
-
from datetime import datetime, timedelta
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class HerbicideAnalyzer:
|
| 13 |
-
"""Classe spécialisée dans l'analyse avancée des herbicides"""
|
| 14 |
-
|
| 15 |
-
def __init__(self, data=None):
|
| 16 |
-
self.df = data
|
| 17 |
-
|
| 18 |
-
def set_data(self, data):
|
| 19 |
-
"""Définit les données à analyser"""
|
| 20 |
-
self.df = data
|
| 21 |
-
|
| 22 |
-
def get_top_ift_parcels_by_year(self, year, n_parcels=10):
|
| 23 |
-
"""
|
| 24 |
-
Retourne les N parcelles avec les IFT herbicides les plus élevés pour une année donnée
|
| 25 |
-
"""
|
| 26 |
-
try:
|
| 27 |
-
if self.df is None or len(self.df) == 0:
|
| 28 |
-
return None, "❌ Aucune donnée disponible"
|
| 29 |
-
|
| 30 |
-
# Filtrer par année et herbicides
|
| 31 |
-
year_data = self.df[
|
| 32 |
-
(self.df['millesime'] == year) &
|
| 33 |
-
(self.df['familleprod'] == 'Herbicides')
|
| 34 |
-
].copy()
|
| 35 |
-
|
| 36 |
-
if len(year_data) == 0:
|
| 37 |
-
return None, f"❌ Aucune donnée d'herbicides pour l'année {year}"
|
| 38 |
-
|
| 39 |
-
# Calculer l'IFT par parcelle
|
| 40 |
-
if 'quantitetot' not in year_data.columns:
|
| 41 |
-
return None, "❌ Colonne 'quantitetot' manquante pour le calcul de l'IFT"
|
| 42 |
-
|
| 43 |
-
# Agrégation par parcelle
|
| 44 |
-
ift_by_parcel = year_data.groupby('numparcell').agg({
|
| 45 |
-
'quantitetot': 'sum',
|
| 46 |
-
'surfparc': 'first',
|
| 47 |
-
'codeinsee': 'first',
|
| 48 |
-
'nomcommune': 'first' if 'nomcommune' in year_data.columns else 'first'
|
| 49 |
-
}).reset_index()
|
| 50 |
-
|
| 51 |
-
# Calcul de l'IFT (quantité totale / surface)
|
| 52 |
-
ift_by_parcel['ift_herbicides'] = ift_by_parcel['quantitetot'] / ift_by_parcel['surfparc']
|
| 53 |
-
|
| 54 |
-
# Top N parcelles
|
| 55 |
-
top_parcels = ift_by_parcel.nlargest(n_parcels, 'ift_herbicides')
|
| 56 |
-
|
| 57 |
-
return top_parcels, f"✅ Top {len(top_parcels)} parcelles avec IFT herbicides élevé trouvées"
|
| 58 |
-
|
| 59 |
-
except Exception as e:
|
| 60 |
-
return None, f"❌ Erreur dans l'analyse des IFT: {str(e)}"
|
| 61 |
-
|
| 62 |
-
def analyze_herbicide_trends_by_region(self, years_range=None):
|
| 63 |
-
"""
|
| 64 |
-
Analyse les tendances d'utilisation d'herbicides par région
|
| 65 |
-
"""
|
| 66 |
-
try:
|
| 67 |
-
if self.df is None or len(self.df) == 0:
|
| 68 |
-
return None, "❌ Aucune donnée disponible"
|
| 69 |
-
|
| 70 |
-
# Filtrer les herbicides
|
| 71 |
-
herbicide_data = self.df[self.df['familleprod'] == 'Herbicides'].copy()
|
| 72 |
-
|
| 73 |
-
if len(herbicide_data) == 0:
|
| 74 |
-
return None, "❌ Aucune donnée d'herbicides trouvée"
|
| 75 |
-
|
| 76 |
-
# Filtrer par années si spécifié
|
| 77 |
-
if years_range:
|
| 78 |
-
herbicide_data = herbicide_data[
|
| 79 |
-
herbicide_data['millesime'].between(years_range[0], years_range[1])
|
| 80 |
-
]
|
| 81 |
-
|
| 82 |
-
# Grouper par région et année
|
| 83 |
-
if 'codeinsee' not in herbicide_data.columns:
|
| 84 |
-
return None, "❌ Colonne 'codeinsee' manquante"
|
| 85 |
-
|
| 86 |
-
regional_trends = herbicide_data.groupby(['codeinsee', 'millesime']).agg({
|
| 87 |
-
'quantitetot': 'sum',
|
| 88 |
-
'surfparc': 'sum',
|
| 89 |
-
'nomcommune': 'first' if 'nomcommune' in herbicide_data.columns else 'first'
|
| 90 |
-
}).reset_index()
|
| 91 |
-
|
| 92 |
-
# Calcul de l'IFT régional
|
| 93 |
-
regional_trends['ift_regional'] = regional_trends['quantitetot'] / regional_trends['surfparc']
|
| 94 |
-
|
| 95 |
-
return regional_trends, f"✅ Analyse des tendances pour {len(regional_trends)} régions/années"
|
| 96 |
-
|
| 97 |
-
except Exception as e:
|
| 98 |
-
return None, f"❌ Erreur dans l'analyse des tendances: {str(e)}"
|
| 99 |
-
|
| 100 |
-
def get_herbicide_usage_statistics(self, year=None):
|
| 101 |
-
"""
|
| 102 |
-
Retourne des statistiques détaillées sur l'utilisation d'herbicides
|
| 103 |
-
"""
|
| 104 |
-
try:
|
| 105 |
-
if self.df is None or len(self.df) == 0:
|
| 106 |
-
return None, "❌ Aucune donnée disponible"
|
| 107 |
-
|
| 108 |
-
herbicide_data = self.df[self.df['familleprod'] == 'Herbicides'].copy()
|
| 109 |
-
|
| 110 |
-
if year:
|
| 111 |
-
herbicide_data = herbicide_data[herbicide_data['millesime'] == year]
|
| 112 |
-
|
| 113 |
-
if len(herbicide_data) == 0:
|
| 114 |
-
return None, f"❌ Aucune donnée d'herbicides pour l'année {year if year else 'toutes'}"
|
| 115 |
-
|
| 116 |
-
stats = {
|
| 117 |
-
'nombre_parcelles': herbicide_data['numparcell'].nunique(),
|
| 118 |
-
'surface_totale': herbicide_data['surfparc'].sum(),
|
| 119 |
-
'quantite_totale': herbicide_data['quantitetot'].sum(),
|
| 120 |
-
'ift_moyen': herbicide_data['quantitetot'].sum() / herbicide_data['surfparc'].sum(),
|
| 121 |
-
'nombre_communes': herbicide_data['codeinsee'].nunique() if 'codeinsee' in herbicide_data.columns else 0,
|
| 122 |
-
'annees_couvertes': sorted(herbicide_data['millesime'].unique().tolist())
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
return stats, "✅ Statistiques calculées avec succès"
|
| 126 |
-
|
| 127 |
-
except Exception as e:
|
| 128 |
-
return None, f"❌ Erreur dans le calcul des statistiques: {str(e)}"
|
| 129 |
-
|
| 130 |
-
def create_herbicide_visualization(self, analysis_type="trends", **kwargs):
|
| 131 |
-
"""
|
| 132 |
-
Crée des visualisations pour l'analyse des herbicides
|
| 133 |
-
"""
|
| 134 |
-
try:
|
| 135 |
-
if analysis_type == "top_ift":
|
| 136 |
-
year = kwargs.get('year', 2023)
|
| 137 |
-
n_parcels = kwargs.get('n_parcels', 10)
|
| 138 |
-
data, msg = self.get_top_ift_parcels_by_year(year, n_parcels)
|
| 139 |
-
|
| 140 |
-
if data is None:
|
| 141 |
-
return self._create_error_plot("Erreur Top IFT", msg)
|
| 142 |
-
|
| 143 |
-
fig = px.bar(
|
| 144 |
-
data,
|
| 145 |
-
x='numparcell',
|
| 146 |
-
y='ift_herbicides',
|
| 147 |
-
title=f'Top {n_parcels} Parcelles - IFT Herbicides {year}',
|
| 148 |
-
labels={'ift_herbicides': 'IFT Herbicides', 'numparcell': 'Numéro Parcelle'}
|
| 149 |
-
)
|
| 150 |
-
|
| 151 |
-
elif analysis_type == "regional_trends":
|
| 152 |
-
years_range = kwargs.get('years_range', None)
|
| 153 |
-
data, msg = self.analyze_herbicide_trends_by_region(years_range)
|
| 154 |
-
|
| 155 |
-
if data is None:
|
| 156 |
-
return self._create_error_plot("Erreur Tendances Régionales", msg)
|
| 157 |
-
|
| 158 |
-
fig = px.line(
|
| 159 |
-
data,
|
| 160 |
-
x='millesime',
|
| 161 |
-
y='ift_regional',
|
| 162 |
-
color='codeinsee',
|
| 163 |
-
title='Tendances IFT Herbicides par Région',
|
| 164 |
-
labels={'ift_regional': 'IFT Régional', 'millesime': 'Année'}
|
| 165 |
-
)
|
| 166 |
-
|
| 167 |
-
else:
|
| 168 |
-
return self._create_error_plot("Type d'analyse non supporté", f"Type '{analysis_type}' non reconnu")
|
| 169 |
-
|
| 170 |
-
fig.update_layout(width=800, height=500)
|
| 171 |
-
return fig
|
| 172 |
-
|
| 173 |
-
except Exception as e:
|
| 174 |
-
return self._create_error_plot("Erreur de visualisation", f"Erreur: {str(e)}")
|
| 175 |
-
|
| 176 |
-
def _create_error_plot(self, title, message):
|
| 177 |
-
"""Crée un graphique d'erreur"""
|
| 178 |
-
fig = go.Figure()
|
| 179 |
-
fig.add_annotation(
|
| 180 |
-
text=message,
|
| 181 |
-
xref="paper", yref="paper",
|
| 182 |
-
x=0.5, y=0.5,
|
| 183 |
-
showarrow=False,
|
| 184 |
-
font=dict(size=14, color="red")
|
| 185 |
-
)
|
| 186 |
-
fig.update_layout(title=title, width=600, height=400)
|
| 187 |
-
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
DELETED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
gradio[mcp]>=5.46.0
|
| 2 |
-
pandas>=2.0.0
|
| 3 |
-
numpy>=1.24.0
|
| 4 |
-
plotly>=5.17.0
|
| 5 |
-
seaborn>=0.12.0
|
| 6 |
-
matplotlib>=3.7.0
|
| 7 |
-
datasets>=2.14.0
|
| 8 |
-
huggingface_hub>=0.17.0
|
| 9 |
-
pyarrow>=12.0.0
|
| 10 |
-
scikit-learn>=1.3.0
|
| 11 |
-
textblob>=0.17.1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
run_server.sh
DELETED
|
@@ -1,30 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
|
| 3 |
-
# Script de lancement du serveur MCP Agriculture
|
| 4 |
-
echo "🌾 Lancement du serveur MCP Agriculture"
|
| 5 |
-
echo "======================================"
|
| 6 |
-
|
| 7 |
-
# Vérifier si Python est disponible
|
| 8 |
-
if ! command -v python3 &> /dev/null; then
|
| 9 |
-
echo "❌ Python3 n'est pas installé"
|
| 10 |
-
exit 1
|
| 11 |
-
fi
|
| 12 |
-
|
| 13 |
-
# Installer les dépendances si nécessaire
|
| 14 |
-
if [ ! -d "venv" ]; then
|
| 15 |
-
echo "📦 Création de l'environnement virtuel..."
|
| 16 |
-
python3 -m venv venv
|
| 17 |
-
source venv/bin/activate
|
| 18 |
-
pip install -r requirements.txt
|
| 19 |
-
else
|
| 20 |
-
echo "📦 Activation de l'environnement virtuel..."
|
| 21 |
-
source venv/bin/activate
|
| 22 |
-
fi
|
| 23 |
-
|
| 24 |
-
# Tester le serveur
|
| 25 |
-
echo "🧪 Test du serveur..."
|
| 26 |
-
python test_mcp_server.py
|
| 27 |
-
|
| 28 |
-
# Lancer le serveur Gradio
|
| 29 |
-
echo "🚀 Lancement du serveur MCP sur http://localhost:7860"
|
| 30 |
-
python app.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_mcp_server.py
DELETED
|
@@ -1,94 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Script de test pour le serveur MCP Agriculture
|
| 3 |
-
"""
|
| 4 |
-
import json
|
| 5 |
-
import sys
|
| 6 |
-
import os
|
| 7 |
-
|
| 8 |
-
# Ajouter le répertoire courant au path pour les imports
|
| 9 |
-
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
| 10 |
-
|
| 11 |
-
from app import AgricultureMCPServer
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
def test_mcp_server():
|
| 15 |
-
"""Test du serveur MCP"""
|
| 16 |
-
print("🧪 Test du serveur MCP Agriculture")
|
| 17 |
-
print("=" * 50)
|
| 18 |
-
|
| 19 |
-
# Initialiser le serveur
|
| 20 |
-
server = AgricultureMCPServer()
|
| 21 |
-
|
| 22 |
-
# Test 1: Chargement des données
|
| 23 |
-
print("📊 Test 1: Chargement des données")
|
| 24 |
-
result = server.load_agriculture_data()
|
| 25 |
-
print(f"Résultat: {result}")
|
| 26 |
-
print()
|
| 27 |
-
|
| 28 |
-
# Test 2: Résumé des données
|
| 29 |
-
print("📋 Test 2: Résumé des données")
|
| 30 |
-
summary = server.get_data_summary()
|
| 31 |
-
try:
|
| 32 |
-
summary_data = json.loads(summary)
|
| 33 |
-
if "error" in summary_data:
|
| 34 |
-
print(f"❌ Erreur: {summary_data['error']}")
|
| 35 |
-
else:
|
| 36 |
-
print(f"✅ Données disponibles: {summary_data.get('nombre_lignes', 0)} lignes")
|
| 37 |
-
print(f" Colonnes: {len(summary_data.get('colonnes', []))}")
|
| 38 |
-
print(f" Années: {summary_data.get('annees_disponibles', [])}")
|
| 39 |
-
except json.JSONDecodeError:
|
| 40 |
-
print(f"❌ Erreur de parsing JSON: {summary}")
|
| 41 |
-
print()
|
| 42 |
-
|
| 43 |
-
# Test 3: Analyse des herbicides
|
| 44 |
-
print("🌿 Test 3: Analyse des herbicides")
|
| 45 |
-
herb_result = server.analyze_herbicide_usage(2023, "statistics")
|
| 46 |
-
try:
|
| 47 |
-
herb_data = json.loads(herb_result)
|
| 48 |
-
if "error" in herb_data:
|
| 49 |
-
print(f"❌ Erreur: {herb_data['error']}")
|
| 50 |
-
else:
|
| 51 |
-
print(f"✅ Analyse réussie: {herb_data.get('status', 'OK')}")
|
| 52 |
-
if "data" in herb_data:
|
| 53 |
-
data = herb_data["data"]
|
| 54 |
-
print(f" Parcelles: {data.get('nombre_parcelles', 0)}")
|
| 55 |
-
print(f" Surface totale: {data.get('surface_totale', 0):.2f}")
|
| 56 |
-
except json.JSONDecodeError:
|
| 57 |
-
print(f"❌ Erreur de parsing JSON: {herb_result}")
|
| 58 |
-
print()
|
| 59 |
-
|
| 60 |
-
# Test 4: Visualisation
|
| 61 |
-
print("📈 Test 4: Création de visualisation")
|
| 62 |
-
try:
|
| 63 |
-
fig = server.create_herbicide_visualization(2023, "top_ift")
|
| 64 |
-
if fig and hasattr(fig, 'data'):
|
| 65 |
-
print("✅ Visualisation créée avec succès")
|
| 66 |
-
print(f" Type: {type(fig).__name__}")
|
| 67 |
-
else:
|
| 68 |
-
print("❌ Échec de création de la visualisation")
|
| 69 |
-
except Exception as e:
|
| 70 |
-
print(f"❌ Erreur lors de la visualisation: {str(e)}")
|
| 71 |
-
print()
|
| 72 |
-
|
| 73 |
-
# Test 5: Requête base de données
|
| 74 |
-
print("🔍 Test 5: Requête base de données")
|
| 75 |
-
query_result = server.query_database("products_by_year", year=2023)
|
| 76 |
-
try:
|
| 77 |
-
query_data = json.loads(query_result)
|
| 78 |
-
if "error" in query_data:
|
| 79 |
-
print(f"❌ Erreur: {query_data['error']}")
|
| 80 |
-
else:
|
| 81 |
-
print(f"✅ Requête réussie pour l'année {query_data.get('year', 2023)}")
|
| 82 |
-
products = query_data.get('products', {})
|
| 83 |
-
print(f" Produits trouvés: {len(products)}")
|
| 84 |
-
for prod, count in list(products.items())[:3]: # Afficher les 3 premiers
|
| 85 |
-
print(f" - {prod}: {count}")
|
| 86 |
-
except json.JSONDecodeError:
|
| 87 |
-
print(f"❌ Erreur de parsing JSON: {query_result}")
|
| 88 |
-
print()
|
| 89 |
-
|
| 90 |
-
print("🎉 Tests terminés!")
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
if __name__ == "__main__":
|
| 94 |
-
test_mcp_server()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|