From e2d1082e92c3fe075748c42619b28d1b6e6fd53b Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Thu, 9 Jul 2026 20:13:17 +0200 Subject: [PATCH] =?UTF-8?q?feat(mcp):=20tool=20search=5Forganisations=20(r?= =?UTF-8?q?=C3=A9solution=20nom=20->=20id)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- src/mcp/queries.py | 37 +++++++++++++++++++++++++++++++++++++ tests/mcp/test_queries.py | 20 ++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/mcp/queries.py diff --git a/src/mcp/queries.py b/src/mcp/queries.py new file mode 100644 index 0000000..cbc9d98 --- /dev/null +++ b/src/mcp/queries.py @@ -0,0 +1,37 @@ +# src/mcp/queries.py +import re + +from src.mcp.serialization import ( + to_json_records, # noqa: F401 (utilisé aux tâches suivantes) +) +from src.utils.data import DF_ACHETEURS, DF_TITULAIRES +from src.utils.search import search_org + +PAGE_SIZE = 50 +TOP_N = 10 +ORG_FRAMES = {"acheteur": DF_ACHETEURS, "titulaire": DF_TITULAIRES} + + +def _extract_plain_text(html_str: str) -> str: + """Extract plain text from HTML link, e.g. '123' -> '123'.""" + match = re.search(r">([^<]+)<", html_str) + return match.group(1) if match else html_str + + +def search_organisations( + query: str, org_type: str = "acheteur", limite: int = 20 +) -> list[dict]: + """Recherche des organisations par nom, résout un nom -> id.""" + if org_type not in ORG_FRAMES: + raise ValueError( + f"type invalide: {org_type!r} (attendu 'acheteur' ou 'titulaire')" + ) + df = search_org(ORG_FRAMES[org_type], query, org_type, track=False).head(limite) + return [ + { + "id": _extract_plain_text(r[f"{org_type}_id"]), + "nom": _extract_plain_text(r[f"{org_type}_nom"]), + "departement": r.get("Département"), + } + for r in df.to_dicts() + ] diff --git a/tests/mcp/test_queries.py b/tests/mcp/test_queries.py index b560a73..8dce3ad 100644 --- a/tests/mcp/test_queries.py +++ b/tests/mcp/test_queries.py @@ -1,4 +1,7 @@ +import pytest + import src.utils.search as search_mod +from src.mcp.queries import search_organisations from src.utils.data import DF_ACHETEURS from src.utils.search import search_org @@ -19,3 +22,20 @@ def test_search_org_track_true_calls_track_search(monkeypatch): search_org(DF_ACHETEURS, "ACHETEUR", "acheteur", track=True) assert calls == [("ACHETEUR", "home_page_search")] + + +def test_search_organisations_finds_known_acheteur(): + result = search_organisations("ACHETEUR", "acheteur") + assert any(r["id"] == "123" for r in result) + first = next(r for r in result if r["id"] == "123") + assert set(first.keys()) == {"id", "nom", "departement"} + + +def test_search_organisations_invalid_type_raises(): + with pytest.raises(ValueError): + search_organisations("x", "autre") + + +def test_search_organisations_respects_limite(): + result = search_organisations("ACHETEUR", "acheteur", limite=1) + assert len(result) <= 1