File size: 2,245 Bytes
089d665
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""Visualization payload — formats the patient subgraph for react-force-graph.

The raras-app /grafo page renders nodes/links via react-force-graph-3d.
This module produces the exact JSON shape that component expects, so the
front-end can drop in `<ForceGraph data={twin.viz_data} />`.

Color map is consistent with raras-app conventions:
  Patient   #00d4ff
  Phenotype #f59e0b
  Disease   #ef4444
  Gene      #10b981
  Drug      #8b5cf6
  Lab       #6366f1
"""
from __future__ import annotations
from typing import Optional

from .types import VizData, Subgraph

_COLOR = {
    "Patient":   "#00d4ff",
    "Phenotype": "#f59e0b",
    "Disease":   "#ef4444",
    "Gene":      "#10b981",
    "Drug":      "#8b5cf6",
    "Lab":       "#6366f1",
    "Snapshot":  "#a78bfa",
}

_GROUP = {
    "Patient": 0, "Phenotype": 1, "Disease": 2, "Gene": 3,
    "Drug": 4, "Lab": 5, "Snapshot": 6,
}

_VAL = {
    "Patient": 30, "Disease": 18, "Gene": 12, "Phenotype": 10,
    "Drug": 14, "Lab": 8, "Snapshot": 9,
}


def from_subgraph(sg: Subgraph, *, center_id: Optional[str] = None) -> VizData:
    """Convert a `Subgraph` into a force-graph-friendly payload."""
    nodes = []
    for n in sg.nodes:
        nodes.append({
            "id": n.id,
            "name": n.name,
            "group": _GROUP.get(n.label, 9),
            "label": n.label,
            "val": _VAL.get(n.label, 8) * (0.5 + n.weight),
            "color": _COLOR.get(n.label, "#94a3b8"),
            "hpo": n.code if n.label == "Phenotype" else None,
            "orpha": n.code if n.label == "Disease" else None,
            "symbol": n.code if n.label == "Gene" else None,
            "rxcui": n.code if n.label == "Drug" else None,
            "weight": n.weight,
            **(n.extra or {}),
        })
    links = []
    for e in sg.edges:
        links.append({
            "source": e.source,
            "target": e.target,
            "label": e.rel,
            "value": max(0.5, e.weight),
            "evidence": e.evidence,
        })

    legend = {label: {"color": color, "group": _GROUP.get(label, 9)} for label, color in _COLOR.items()}

    return VizData(
        nodes=nodes,
        links=links,
        center_id=center_id,
        legend=legend,
    )