Files
PlantAI-dev/apps/cli/string_api.py
T
2026-08-03 18:42:47 +08:00

204 lines
6.3 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
STRING Database API Client
用于获取蛋白互作网络数据
"""
import requests
import time
from pathlib import Path
from typing import List, Dict, Any, Optional
import json
from gfa_core.ppi_string_policy import STRING_SPECIES_MAP, string_taxon_id
class StringAPI:
"""STRING数据库API客户端"""
BASE_URL = "https://string-db.org/api"
SPECIES_MAP = STRING_SPECIES_MAP
def __init__(self, species: str = "ath", timeout: int = 30):
"""
初始化STRING API客户端
Args:
species: 物种代码(ath, osa, zma等)
timeout: 请求超时时间(秒)
"""
self.species = species
self.species_id = string_taxon_id(species, "3702")
self.timeout = timeout
self.session = requests.Session()
def get_network(self,
proteins: List[str],
required_score: int = 200,
network_type: str = "functional") -> Dict[str, Any]:
"""
获取蛋白互作网络
Args:
proteins: 蛋白ID列表
required_score: 互作置信度阈值(0-1000,默认200=0.7)
network_type: 网络类型(functional/physical)
Returns:
互作网络数据
"""
if not proteins:
return {"error": "No proteins provided"}
# STRING API限制:每次最多2000个蛋白
if len(proteins) > 2000:
proteins = proteins[:2000]
print(f"[WARNING] Protein list truncated to 2000 (STRING API limit)")
params = {
"identifiers": "\r".join(proteins),
"species": self.species_id,
"required_score": required_score,
"network_type": network_type,
"caller_identity": "BotanicalBaseAgent"
}
try:
response = self.session.get(
f"{self.BASE_URL}/json/network",
params=params,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": f"API request failed: {str(e)}"}
def get_interaction_partners(self,
protein: str,
required_score: int = 200,
limit: int = 100) -> Dict[str, Any]:
"""
获取指定蛋白的互作伙伴
Args:
protein: 蛋白ID
required_score: 互作置信度阈值
limit: 返回的最大互作数
Returns:
互作伙伴数据
"""
params = {
"identifiers": protein,
"species": self.species_id,
"required_score": required_score,
"limit": limit,
"caller_identity": "BotanicalBaseAgent"
}
try:
response = self.session.get(
f"{self.BASE_URL}/json/interaction_partners",
params=params,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": f"API request failed: {str(e)}"}
def get_protein_info(self, proteins: List[str]) -> Dict[str, Any]:
"""
获取蛋白详细信息
Args:
proteins: 蛋白ID列表
Returns:
蛋白信息数据
"""
if not proteins:
return {"error": "No proteins provided"}
params = {
"identifiers": "\r".join(proteins),
"species": self.species_id,
"caller_identity": "BotanicalBaseAgent"
}
try:
response = self.session.get(
f"{self.BASE_URL}/json/protein_info",
params=params,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": f"API request failed: {str(e)}"}
def save_network_to_file(self,
network_data: Dict[str, Any],
output_file: Path,
format: str = "tsv"):
"""
保存网络数据到文件
Args:
network_data: 网络数据
output_file: 输出文件路径
format: 输出格式(tsv/json)
"""
output_file = Path(output_file)
output_file.parent.mkdir(parents=True, exist_ok=True)
if format == "json":
with open(output_file, 'w') as f:
json.dump(network_data, f, indent=2)
elif format == "tsv":
# 转换为边列表格式
edges = []
for interaction in network_data:
if isinstance(interaction, dict):
edges.append({
"protein1": interaction.get("preferredName_A", ""),
"protein2": interaction.get("preferredName_B", ""),
"score": interaction.get("score", 0),
"ncbi_tax_id": interaction.get("ncbiTaxonId", ""),
})
import pandas as pd
df = pd.DataFrame(edges)
df.to_csv(output_file, sep="\t", index=False)
print(f"[INFO] Network saved to: {output_file}")
def test_string_api():
"""测试STRING API"""
api = StringAPI(species="ath")
# 测试基因
test_genes = ["AT1G01010", "AT1G01020", "AT1G01030"]
print("Testing STRING API...")
print(f"Species: {api.species} (ID: {api.species_id})")
print(f"Test genes: {test_genes}")
# 获取网络
network = api.get_network(test_genes, required_score=400)
if "error" in network:
print(f"Error: {network['error']}")
else:
print(f"Interactions found: {len(network)}")
if network:
print("Sample interaction:")
print(json.dumps(network[0], indent=2))
if __name__ == "__main__":
test_string_api()