MCP_CO2 / src /clients /agribalyse.py
Qdonnars's picture
MCP Server ADEME Environnement - Initial release
aac4780
"""Client API AGRIBALYSE - Données environnementales alimentaires."""
import httpx
from typing import Any
BASE_URL = "https://data.ademe.fr/data-fair/api/v1/datasets/agribalyse-31-synthese"
class AgribalyseClient:
"""Client async pour l'API AGRIBALYSE."""
def __init__(self, timeout: float = 30.0):
self.timeout = timeout
self._client: httpx.AsyncClient | None = None
async def _get_client(self) -> httpx.AsyncClient:
if self._client is None:
self._client = httpx.AsyncClient(timeout=self.timeout)
return self._client
async def close(self):
if self._client:
await self._client.aclose()
self._client = None
async def search(
self,
query: str,
size: int = 10,
select: list[str] | None = None
) -> dict[str, Any]:
"""Recherche d'aliments dans AGRIBALYSE.
Args:
query: Terme de recherche (ex: "boeuf", "pomme")
size: Nombre de résultats (max 100)
select: Champs à retourner (None = tous)
Returns:
Dict avec total et results
"""
client = await self._get_client()
params = {
"q": query,
"size": min(size, 100)
}
if select:
params["select"] = ",".join(select)
response = await client.get(f"{BASE_URL}/lines", params=params)
response.raise_for_status()
return response.json()
async def get_by_code(self, code_ciqual: int) -> dict[str, Any] | None:
"""Récupère un aliment par son code CIQUAL.
Args:
code_ciqual: Code CIQUAL de l'aliment
Returns:
Données de l'aliment ou None
"""
client = await self._get_client()
params = {
"qs": f"Code_CIQUAL:{code_ciqual}",
"size": 1
}
response = await client.get(f"{BASE_URL}/lines", params=params)
response.raise_for_status()
data = response.json()
if data.get("results"):
return data["results"][0]
return None
async def get_categories(self) -> list[str]:
"""Liste les groupes d'aliments disponibles.
Returns:
Liste des groupes d'aliments uniques
"""
client = await self._get_client()
response = await client.get(
f"{BASE_URL}/values_agg",
params={"field": "Groupe_d'aliment", "size": 100}
)
response.raise_for_status()
data = response.json()
return [item["value"] for item in data.get("aggs", [])]
async def get_subcategories(self, group: str) -> list[str]:
"""Liste les sous-groupes d'un groupe d'aliments.
Args:
group: Nom du groupe d'aliments
Returns:
Liste des sous-groupes
"""
client = await self._get_client()
response = await client.get(
f"{BASE_URL}/values_agg",
params={
"field": "Sous-groupe_d'aliment",
"size": 100,
"qs": f"Groupe_d'aliment:\"{group}\""
}
)
response.raise_for_status()
data = response.json()
return [item["value"] for item in data.get("aggs", [])]
# Champs principaux AGRIBALYSE pour référence
AGRIBALYSE_FIELDS = {
"identite": [
"Code_CIQUAL",
"Code_AGB",
"Nom_du_Produit_en_Français",
"LCI_Name",
"Groupe_d'aliment",
"Sous-groupe_d'aliment",
],
"impact_climatique": [
"Changement_climatique",
"Changement_climatique_-_émissions_fossiles",
"Changement_climatique_-_émissions_biogéniques",
"Changement_climatique_-_émissions_liées_au_changement_d'affectation_des_sols",
],
"autres_impacts": [
"Score_unique_EF",
"Acidification_terrestre_et_eaux_douces",
"Eutrophisation_marine",
"Eutrophisation_eaux_douces",
"Eutrophisation_terrestre",
"Utilisation_du_sol",
"Épuisement_des_ressources_eau",
"Épuisement_des_ressources_énergétiques",
"Épuisement_des_ressources_minéraux",
"Particules_fines",
"Formation_photochimique_d'ozone",
"Appauvrissement_de_la_couche_d'ozone",
"Rayonnements_ionisants",
"Écotoxicité_pour_écosystèmes_aquatiques_d'eau_douce",
"Effets_toxicologiques_sur_la_santé_humaine___substances_cancérogènes",
"Effets_toxicologiques_sur_la_santé_humaine___substances_non-cancérogènes",
],
"qualite": [
"DQR",
],
"contexte": [
"Livraison",
"Préparation",
"Approche_emballage_",
"code_avion",
"code_saison",
],
}