File size: 5,367 Bytes
faef703
5f71d94
 
 
 
 
937ad52
5f71d94
 
 
937ad52
 
5f71d94
 
 
 
 
 
937ad52
 
 
 
 
 
 
 
 
 
 
 
 
 
5f71d94
 
 
 
 
 
 
 
 
 
 
 
937ad52
5f71d94
 
 
937ad52
 
 
 
 
 
5f71d94
937ad52
 
5f71d94
937ad52
 
 
 
 
 
 
 
5f71d94
937ad52
 
 
 
 
 
5f71d94
937ad52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f71d94
937ad52
 
5f71d94
faef703
937ad52
 
 
faef703
937ad52
 
 
 
faef703
937ad52
 
5f71d94
 
937ad52
 
5f71d94
 
 
937ad52
faef703
937ad52
faef703
937ad52
 
 
5f71d94
faef703
937ad52
faef703
937ad52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f71d94
 
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# genesis/visualization.py
"""
Visualization utilities for GENESIS-AI
Generates interactive pathway and funding network graphs from Neo4j data.
"""

import os
import logging
from typing import List, Tuple, Optional

import plotly.graph_objects as go

# Optional Neo4j import
try:
    from neo4j import GraphDatabase
except ImportError:
    GraphDatabase = None

# =========================
# CONFIGURATION
# =========================
NEO4J_URI = os.getenv("NEO4J_URI")
NEO4J_USER = os.getenv("NEO4J_USER")
NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD")

COLOR_PATHWAY_NODE = "#00FF99"  # Biotech green
COLOR_FUNDING_NODE = "#FF9900"  # Orange
COLOR_EDGE = "#AAAAAA"

# =========================
# NEO4J CONNECTION
# =========================
driver = None
if GraphDatabase and NEO4J_URI and NEO4J_USER and NEO4J_PASSWORD:
    try:
        driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))
        logging.info("[Neo4j] Connected for visualization.")
    except Exception as e:
        logging.error(f"[Neo4j] Connection failed: {e}")
        driver = None
else:
    logging.info("[Neo4j] No URI/user/password set β€” skipping graph_tools connection.")

def run_neo4j_query(query: str, params: dict = None) -> list:
    """Run a Cypher query and return results."""
    if not driver:
        logging.warning("[Neo4j] No active connection β€” returning empty result.")
        return []
    with driver.session() as session:
        return list(session.run(query, params or {}))

# =========================
# GRAPH UTILS
# =========================
def create_plotly_graph(nodes: List[Tuple[str, str, str]], edges: List[Tuple[str, str]], title: str):
    """Creates an interactive Plotly network graph."""
    import networkx as nx

    G = nx.Graph()
    for node_id, label, color in nodes:
        G.add_node(node_id, label=label, color=color)
    for src, dst in edges:
        G.add_edge(src, dst)

    pos = nx.spring_layout(G, seed=42, k=0.5)

    edge_x, edge_y = [], []
    for src, dst in edges:
        x0, y0 = pos[src]
        x1, y1 = pos[dst]
        edge_x.extend([x0, x1, None])
        edge_y.extend([y0, y1, None])

    node_x, node_y, node_text, node_color = [], [], [], []
    for node_id, label, color in nodes:
        x, y = pos[node_id]
        node_x.append(x)
        node_y.append(y)
        node_text.append(label)
        node_color.append(color)

    edge_trace = go.Scatter(
        x=edge_x, y=edge_y,
        line=dict(width=1, color=COLOR_EDGE),
        hoverinfo='none',
        mode='lines'
    )

    node_trace = go.Scatter(
        x=node_x, y=node_y,
        mode='markers+text',
        text=node_text,
        textposition="top center",
        hoverinfo="text",
        marker=dict(
            color=node_color,
            size=18,
            line=dict(width=2, color='#000000')
        )
    )

    fig = go.Figure(data=[edge_trace, node_trace])
    fig.update_layout(
        title=title,
        title_x=0.5,
        plot_bgcolor="#111111",
        paper_bgcolor="#111111",
        font=dict(color="#FFFFFF"),
        showlegend=False,
        margin=dict(l=10, r=10, t=40, b=10)
    )
    return fig

# =========================
# PATHWAY GRAPH
# =========================
def generate_pathway_graph(pathway_name: str) -> Optional[go.Figure]:
    """
    Generate an interactive graph for a given metabolic pathway.
    Only takes pathway_name β€” matches pipeline.py signature.
    """
    query = """
    MATCH (p:Pathway {name: $name})-[r:INVOLVES]->(m:Molecule)
    RETURN p.name AS pathway, m.name AS molecule
    """
    results = run_neo4j_query(query, {"name": pathway_name})

    if not results:
        return None

    nodes = [(pathway_name, pathway_name, COLOR_PATHWAY_NODE)]
    edges = []
    seen_nodes = {pathway_name}

    for record in results:
        mol_name = record["molecule"]
        if mol_name not in seen_nodes:
            nodes.append((mol_name, mol_name, "#00BFFF"))  # Blue for molecules
            seen_nodes.add(mol_name)
        edges.append((pathway_name, mol_name))

    return create_plotly_graph(nodes, edges, f"Metabolic Pathway: {pathway_name}")

# =========================
# FUNDING NETWORK
# =========================
def generate_funding_network(industry_keyword: str) -> Optional[go.Figure]:
    """
    Generate an interactive funding network graph for companies in a given biotech domain.
    """
    query = """
    MATCH (c:Company)-[f:FUNDED_BY]->(i:Investor)
    WHERE toLower(c.industry) CONTAINS toLower($keyword)
    RETURN c.name AS company, i.name AS investor
    """
    results = run_neo4j_query(query, {"keyword": industry_keyword})

    if not results:
        return None

    nodes = []
    edges = []
    seen_nodes = set()

    for record in results:
        comp = record["company"]
        inv = record["investor"]

        if comp not in seen_nodes:
            nodes.append((comp, comp, COLOR_FUNDING_NODE))
            seen_nodes.add(comp)
        if inv not in seen_nodes:
            nodes.append((inv, inv, "#FFD700"))  # Gold for investors
            seen_nodes.add(inv)

        edges.append((comp, inv))

    return create_plotly_graph(nodes, edges, f"Funding Network: {industry_keyword}")

# =========================
# CLEANUP
# =========================
def close_driver():
    if driver:
        driver.close()