Source code for tooluniverse.regulomedb_tool

import requests
from typing import Any, Dict
from .base_tool import BaseTool
from .tool_registry import register_tool


[docs] @register_tool("RegulomeDBRESTTool") class RegulomeDBRESTTool(BaseTool):
[docs] def __init__(self, tool_config: Dict): super().__init__(tool_config) self.base_url = "https://regulomedb.org" self.session = requests.Session() self.session.headers.update({"Accept": "application/json"}) self.timeout = 30
[docs] def _build_url(self, args: Dict[str, Any]) -> str: url = self.tool_config["fields"]["endpoint"] for k, v in args.items(): url = url.replace(f"{{{k}}}", str(v)) return url
[docs] def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: # Accept 'variant' as alias for 'rsid' (used by RegulomeDB_get_score) if "rsid" not in arguments and arguments.get("variant"): arguments = dict(arguments, rsid=arguments["variant"]) try: url = self._build_url(arguments) response = self.session.get(url, timeout=self.timeout) response.raise_for_status() raw = response.json() except Exception as e: return {"status": "error", "error": f"RegulomeDB API error: {str(e)}"} # Extract the key RegulomeDB fields; @graph contains raw ENCODE datasets # (hundreds of entries) and is not useful for most callers. variants = raw.get("variants", []) if not variants: return { "status": "error", "error": f"No RegulomeDB results for rsID '{arguments.get('rsid', '')}'", } result = { "rsid": arguments.get("rsid", ""), "assembly": raw.get("assembly"), "query_coordinates": raw.get("query_coordinates"), "regulome_score": raw.get("regulome_score"), "variants": variants, "features": raw.get("features"), "nearby_snps": raw.get("nearby_snps", [])[:10], "notifications": raw.get("notifications"), "total_supporting_datasets": len(raw.get("@graph", [])), } result.update(self._summarize_motifs(raw.get("@graph", []))) return {"status": "success", "data": result, "url": url}
# RegulomeDB records a motif hit under two methods: "PWMs" (the position # weight matrix matched here) and "footprints" (a DNase footprint was called # for that factor here). The site's Motifs tab lists the union of the two, # one row per target label -- so PWM rows alone undercount it. `features` # only carries booleans (`PWM: true`), which cannot answer "how many motifs # are annotated here" at all. MOTIF_METHODS = ("PWMs", "footprints")
[docs] @staticmethod def _summarize_motifs(graph): """Motif annotations from the evidence graph, matching the Motifs tab. Target labels are kept verbatim: RegulomeDB emits a combined label such as "STAT5A, STAT5B" for a matrix shared by two factors, and the site shows it as a single motif row. Splitting it would undercount. """ rows = [] for entry in graph or []: method = entry.get("method") if method not in RegulomeDBRESTTool.MOTIF_METHODS: continue rows.append( { "target_label": entry.get("target_label"), "method": method, "chrom": entry.get("chrom"), "start": entry.get("start"), "end": entry.get("end"), "strand": entry.get("strand"), } ) by_target = {} for row in rows: target = row["target_label"] slot = by_target.setdefault( target, {"target_label": target, "methods": set(), "n_records": 0} ) slot["methods"].add(row["method"]) slot["n_records"] += 1 motifs = [ { "target_label": v["target_label"], "methods": sorted(v["methods"]), "n_records": v["n_records"], } for v in by_target.values() ] motifs.sort(key=lambda m: str(m["target_label"] or "")) return { "motifs": motifs, "motif_count": len(motifs), "motif_records": len(rows), "motif_note": ( "motif_count is the number of distinct motif target labels across " "PWM and footprint evidence, matching the RegulomeDB Motifs tab. " "A combined label such as 'STAT5A, STAT5B' counts as one motif." ), }