Source code for tooluniverse.mpd_tool
import requests
from typing import Any, Dict
from urllib.parse import quote
from .base_tool import BaseTool
from .tool_registry import register_tool
[docs]
@register_tool("MPDRESTTool")
class MPDRESTTool(BaseTool):
[docs]
def __init__(self, tool_config: Dict):
super().__init__(tool_config)
self.session = requests.Session()
self.session.headers.update({"Accept": "application/json"})
self.timeout = 30
[docs]
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
try:
# Use ENCODE as alternative data source for biological samples.
# MPD's own REST API (phenome.jax.org/api) has no endpoint that
# takes a strain name + phenotype category directly -- confirmed
# live, its phenotype endpoints require a measure ID or project
# symbol the caller doesn't supply here.
strain = arguments.get("strain", "C57BL/6J")
limit = arguments.get("limit", 10)
# searchTerm is a free-text match, unlike biosample_ontology.term_name
# (a tissue/cell-type ontology field, not a strain field) -- confirmed
# live the latter always returns zero results for real strain names,
# while searchTerm correctly surfaces strain-mentioning experiments
# (e.g. 292 hits for "C57BL/6J") and zero for strains ENCODE has no
# data on (e.g. "DBA/2J").
url = (
"https://www.encodeproject.org/search/?type=Experiment"
f"&searchTerm={quote(str(strain))}&format=json&limit={limit}"
)
response = self.session.get(url, timeout=self.timeout)
# ENCODE answers a search with no hits with 404, not an empty
# result set. DBA/2J has no ENCODE data -- the comment above says
# so, having checked it -- so Ex 3 of this tool's own examples
# turned "nothing matched" into "MPD API error: 404 Client Error".
# A search that found nothing is a successful search.
if response.status_code == 404:
# The 404 body is a complete ENCODE search response --
# @context "/terms/", @graph [], total 0, notification
# "No results found" -- so it is handed back as-is rather than
# synthesised, which also keeps it inside the declared schema.
try:
empty = response.json()
except ValueError:
empty = {"@context": "/terms/", "@graph": [], "total": 0}
return {
"status": "success",
"data": empty,
"url": url,
"query_info": {
"strain": strain,
"limit": limit,
"data_source": "ENCODE (MPD alternative)",
"note": (
f"ENCODE has no experiment mentioning {strain!r}. "
"Its search endpoint reports no hits as HTTP 404, so "
"this is an empty result rather than a failure. For "
"curated mouse phenotype data, query the Mouse "
"Phenome Database directly at "
"https://phenome.jax.org/api."
),
},
}
response.raise_for_status()
# Parse JSON response
data = response.json()
return {
"status": "success",
"data": data,
"url": url,
"query_info": {
"strain": strain,
"limit": limit,
"data_source": "ENCODE (MPD alternative)",
"note": (
"ENCODE has no mouse-phenotype-by-strain data model, so "
"results are general ENCODE experiments whose free text "
"mentions this strain, not curated MPD phenotype "
"measurements. 'phenotype_category' is not applied -- "
"ENCODE has no equivalent concept. For genuine curated "
"phenotype data, query the Mouse Phenome Database "
"directly at https://phenome.jax.org/api."
),
},
}
except Exception as e:
return {"status": "error", "error": f"MPD API error: {str(e)}"}