Create knowledge_graph.py
Browse files- mcp/knowledge_graph.py +59 -0
mcp/knowledge_graph.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# mcp/knowledge_graph.py
|
| 2 |
+
|
| 3 |
+
from pyvis.network import Network
|
| 4 |
+
import tempfile
|
| 5 |
+
|
| 6 |
+
def build_knowledge_graph(papers, umls, drugs):
|
| 7 |
+
"""
|
| 8 |
+
Build and return a pyvis Network object representing the knowledge graph.
|
| 9 |
+
Nodes: papers, UMLS concepts, drugs.
|
| 10 |
+
Edges: paper–concept, paper–drug, drug–concept.
|
| 11 |
+
"""
|
| 12 |
+
net = Network(height="550px", width="100%", bgcolor="#f3eaff", font_color="#222222", notebook=False, directed=False)
|
| 13 |
+
net.force_atlas_2based() # More visually appealing layout
|
| 14 |
+
|
| 15 |
+
# Add UMLS concept nodes
|
| 16 |
+
concept_nodes = {}
|
| 17 |
+
for concept in umls:
|
| 18 |
+
if concept["cui"]:
|
| 19 |
+
node_id = f"concept_{concept['cui']}"
|
| 20 |
+
net.add_node(node_id, label=concept["name"], color="#00b894", title=f"UMLS: {concept['cui']}\n{concept['definition'] or ''}")
|
| 21 |
+
concept_nodes[concept['name']] = node_id
|
| 22 |
+
|
| 23 |
+
# Add drug nodes
|
| 24 |
+
drug_nodes = {}
|
| 25 |
+
for drug_report in drugs:
|
| 26 |
+
if isinstance(drug_report, list):
|
| 27 |
+
# OpenFDA can return a list of dicts
|
| 28 |
+
for d in drug_report:
|
| 29 |
+
if "safety_report_id" in d:
|
| 30 |
+
node_id = f"drug_{d['safety_report_id']}"
|
| 31 |
+
drug_name = d.get('reactions', ['drug'])[0] if d.get('reactions') else 'drug'
|
| 32 |
+
net.add_node(node_id, label=drug_name, color="#d35400", title=str(d))
|
| 33 |
+
drug_nodes[drug_name] = node_id
|
| 34 |
+
|
| 35 |
+
# Add paper nodes and edges to concepts and drugs
|
| 36 |
+
for i, paper in enumerate(papers, 1):
|
| 37 |
+
paper_id = f"paper_{i}"
|
| 38 |
+
net.add_node(paper_id, label=paper["title"][:40] + "...", color="#0984e3", title=f"{paper['title']}\n{paper['summary']}\n{paper['link']}")
|
| 39 |
+
|
| 40 |
+
# Link paper to concepts by presence in summary/title
|
| 41 |
+
for cname, concept_id in concept_nodes.items():
|
| 42 |
+
if cname.lower() in (paper["summary"] + paper["title"]).lower():
|
| 43 |
+
net.add_edge(paper_id, concept_id, color="#00b894")
|
| 44 |
+
|
| 45 |
+
# Link paper to drugs if drug name is present in summary/title
|
| 46 |
+
for dname, drug_id in drug_nodes.items():
|
| 47 |
+
if dname.lower() in (paper["summary"] + paper["title"]).lower():
|
| 48 |
+
net.add_edge(paper_id, drug_id, color="#d35400")
|
| 49 |
+
|
| 50 |
+
# Optionally: link concepts to drugs if name overlap
|
| 51 |
+
for cname, concept_id in concept_nodes.items():
|
| 52 |
+
for dname, drug_id in drug_nodes.items():
|
| 53 |
+
if cname.lower() in dname.lower() or dname.lower() in cname.lower():
|
| 54 |
+
net.add_edge(concept_id, drug_id, color="#636e72")
|
| 55 |
+
|
| 56 |
+
# Save and return path to HTML
|
| 57 |
+
temp_path = tempfile.NamedTemporaryFile(delete=False, suffix=".html").name
|
| 58 |
+
net.show(temp_path)
|
| 59 |
+
return temp_path
|