baconnier commited on
Commit
fad8082
·
verified ·
1 Parent(s): bbb7597

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -95
app.py CHANGED
@@ -22,89 +22,193 @@ except Exception as e:
22
  raise
23
 
24
  def create_interactive_graph(nodes):
25
- """Create an interactive graph visualization using networkx and plotly"""
26
- G = nx.DiGraph()
27
-
28
- # Add nodes
29
- for node in nodes:
30
- G.add_node(
31
- node['id'],
32
- title=node['title'],
33
- description=node['description'],
34
- depth=node['depth']
35
- )
36
-
37
- # Add edges
38
- for node in nodes:
39
- for conn in node.get('connections', []):
40
- G.add_edge(
41
- node['id'],
42
- conn['target_id'],
43
- weight=conn.get('relevance_score', 1)
44
- )
45
-
46
- # Create layout
47
- pos = nx.spring_layout(G, k=1, iterations=50)
48
-
49
- # Create node traces
50
- node_x = []
51
- node_y = []
52
- node_text = []
53
- node_color = []
54
-
55
- for node in G.nodes():
56
- x, y = pos[node]
57
- node_x.append(x)
58
- node_y.append(y)
59
- node_text.append(f"{G.nodes[node]['title']}<br>{G.nodes[node]['description']}")
60
- node_color.append(['#FF9999', '#99FF99', '#9999FF'][G.nodes[node]['depth'] % 3])
61
-
62
- node_trace = go.Scatter(
63
- x=node_x,
64
- y=node_y,
65
- mode='markers+text',
66
- hoverinfo='text',
67
- text=[G.nodes[node]['title'] for node in G.nodes()],
68
- hovertext=node_text,
69
- marker=dict(
70
- color=node_color,
71
- size=30,
72
- line=dict(width=2)
73
- )
74
- )
75
-
76
- # Create edge traces
77
- edge_x = []
78
- edge_y = []
79
-
80
- for edge in G.edges():
81
- x0, y0 = pos[edge[0]]
82
- x1, y1 = pos[edge[1]]
83
- edge_x.extend([x0, x1, None])
84
- edge_y.extend([y0, y1, None])
85
-
86
- edge_trace = go.Scatter(
87
- x=edge_x,
88
- y=edge_y,
89
- line=dict(width=1, color='#888'),
90
- hoverinfo='none',
91
- mode='lines'
92
- )
93
-
94
- # Create figure
95
- fig = go.Figure(
96
- data=[edge_trace, node_trace],
97
- layout=go.Layout(
98
- showlegend=False,
99
- hovermode='closest',
100
- margin=dict(b=0, l=0, r=0, t=0),
101
- xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
102
- yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
103
- )
104
- )
105
-
106
- return fig
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  def summarize_node(node_data: Dict[str, Any]) -> str:
109
  """Generate a summary for a selected node"""
110
  summary = f"""
@@ -215,7 +319,7 @@ def create_interface() -> gr.Blocks:
215
  with gr.Accordion("Exploration Result", open=False):
216
  text_output = gr.JSON(label="Raw Result")
217
 
218
- graph_output = gr.Plot(label="Interactive Exploration Graph")
219
  node_summary = gr.Textbox(
220
  label="Node Details",
221
  lines=5,
@@ -228,19 +332,13 @@ def create_interface() -> gr.Blocks:
228
  outputs=[text_output, graph_output, node_summary]
229
  )
230
 
231
- gr.Markdown("""
232
- ### Examples
233
- Click an example to load it into the interface.
234
- """)
235
-
236
- examples = [
237
- ["Explore the evolution of Renaissance painting techniques", "[]", "{}", 5, "Renaissance"],
238
- ["Investigate the influence of Japanese art on Impressionism", "[]", "{}", 7, "Impressionism"],
239
- ["Analyze the development of Cubism through Picasso's work", "[]", "{}", 6, "Cubism"]
240
- ]
241
-
242
  gr.Examples(
243
- examples=examples,
 
 
 
 
244
  inputs=[query_input, path_history, parameters, depth, domain]
245
  )
246
 
 
22
  raise
23
 
24
  def create_interactive_graph(nodes):
25
+ """Create an interactive graph visualization using D3.js"""
26
+ html_content = """
27
+ <div id="network-container" style="width: 100%; height: 600px; border: 1px solid #ddd; border-radius: 4px; background-color: #ffffff;"></div>
28
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
29
+ <style>
30
+ .node { cursor: pointer; }
31
+ .node text {
32
+ font-size: 12px;
33
+ font-family: Arial, sans-serif;
34
+ }
35
+ .link {
36
+ stroke: #999;
37
+ stroke-opacity: 0.6;
38
+ }
39
+ .node-tooltip {
40
+ position: absolute;
41
+ padding: 8px;
42
+ background: rgba(0, 0, 0, 0.8);
43
+ color: #fff;
44
+ border-radius: 4px;
45
+ font-size: 12px;
46
+ pointer-events: none;
47
+ }
48
+ </style>
49
+ <script>
50
+ (function() {
51
+ // Convert Python data to JavaScript
52
+ const data = {
53
+ nodes: """ + json.dumps([{
54
+ 'id': node['id'],
55
+ 'title': node['title'],
56
+ 'description': node['description'],
57
+ 'depth': node['depth']
58
+ } for node in nodes]) + """,
59
+ links: """ + json.dumps([{
60
+ 'source': node['id'],
61
+ 'target': conn['target_id'],
62
+ 'value': conn.get('relevance_score', 1)
63
+ } for node in nodes for conn in node.get('connections', [])]) + """
64
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ // Clear any existing visualization
67
+ const container = document.getElementById('network-container');
68
+ container.innerHTML = '';
69
+
70
+ const width = container.clientWidth;
71
+ const height = container.clientHeight;
72
+
73
+ const svg = d3.select("#network-container")
74
+ .append("svg")
75
+ .attr("width", width)
76
+ .attr("height", height);
77
+
78
+ // Add zoom behavior
79
+ const g = svg.append("g");
80
+ svg.call(d3.zoom()
81
+ .scaleExtent([0.1, 4])
82
+ .on("zoom", (event) => {
83
+ g.attr("transform", event.transform);
84
+ }));
85
+
86
+ // Create the force simulation
87
+ const simulation = d3.forceSimulation(data.nodes)
88
+ .force("link", d3.forceLink(data.links).id(d => d.id).distance(100))
89
+ .force("charge", d3.forceManyBody().strength(-300))
90
+ .force("center", d3.forceCenter(width / 2, height / 2))
91
+ .force("collision", d3.forceCollide().radius(50));
92
+
93
+ // Create the links
94
+ const link = g.append("g")
95
+ .selectAll("line")
96
+ .data(data.links)
97
+ .join("line")
98
+ .attr("class", "link")
99
+ .attr("stroke-width", d => Math.sqrt(d.value || 1));
100
+
101
+ // Create the nodes
102
+ const node = g.append("g")
103
+ .selectAll(".node")
104
+ .data(data.nodes)
105
+ .join("g")
106
+ .attr("class", "node")
107
+ .call(d3.drag()
108
+ .on("start", dragstarted)
109
+ .on("drag", dragged)
110
+ .on("end", dragended));
111
+
112
+ // Add circles to nodes
113
+ node.append("circle")
114
+ .attr("r", 20)
115
+ .attr("fill", d => ['#FF9999', '#99FF99', '#9999FF'][d.depth % 3]);
116
+
117
+ // Add text labels
118
+ node.append("text")
119
+ .attr("dy", 30)
120
+ .attr("text-anchor", "middle")
121
+ .text(d => d.title)
122
+ .call(wrap, 60);
123
+
124
+ // Create tooltip
125
+ const tooltip = d3.select("body")
126
+ .append("div")
127
+ .attr("class", "node-tooltip")
128
+ .style("opacity", 0);
129
+
130
+ // Add hover effects
131
+ node.on("mouseover", function(event, d) {
132
+ tooltip.transition()
133
+ .duration(200)
134
+ .style("opacity", .9);
135
+ tooltip.html(`Title: ${d.title}<br/>Description: ${d.description}`)
136
+ .style("left", (event.pageX + 10) + "px")
137
+ .style("top", (event.pageY - 10) + "px");
138
+ })
139
+ .on("mouseout", function(d) {
140
+ tooltip.transition()
141
+ .duration(500)
142
+ .style("opacity", 0);
143
+ });
144
+
145
+ // Add click handler
146
+ node.on("click", function(event, d) {
147
+ if (window.gradio_client) {
148
+ window.gradio_client.dispatch("select", d);
149
+ }
150
+ });
151
+
152
+ // Update positions on each tick
153
+ simulation.on("tick", () => {
154
+ link
155
+ .attr("x1", d => d.source.x)
156
+ .attr("y1", d => d.source.y)
157
+ .attr("x2", d => d.target.x)
158
+ .attr("y2", d => d.target.y);
159
+
160
+ node
161
+ .attr("transform", d => `translate(${d.x},${d.y})`);
162
+ });
163
+
164
+ // Drag functions
165
+ function dragstarted(event) {
166
+ if (!event.active) simulation.alphaTarget(0.3).restart();
167
+ event.subject.fx = event.subject.x;
168
+ event.subject.fy = event.subject.y;
169
+ }
170
+
171
+ function dragged(event) {
172
+ event.subject.fx = event.x;
173
+ event.subject.fy = event.y;
174
+ }
175
+
176
+ function dragended(event) {
177
+ if (!event.active) simulation.alphaTarget(0);
178
+ event.subject.fx = null;
179
+ event.subject.fy = null;
180
+ }
181
+
182
+ // Text wrapping function
183
+ function wrap(text, width) {
184
+ text.each(function() {
185
+ const text = d3.select(this);
186
+ const words = text.text().split(/\s+/).reverse();
187
+ let word;
188
+ let line = [];
189
+ let lineNumber = 0;
190
+ const lineHeight = 1.1;
191
+ const y = text.attr("y");
192
+ const dy = parseFloat(text.attr("dy"));
193
+ let tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "px");
194
+
195
+ while (word = words.pop()) {
196
+ line.push(word);
197
+ tspan.text(line.join(" "));
198
+ if (tspan.node().getComputedTextLength() > width) {
199
+ line.pop();
200
+ tspan.text(line.join(" "));
201
+ line = [word];
202
+ tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "px").text(word);
203
+ }
204
+ }
205
+ });
206
+ }
207
+ })();
208
+ </script>
209
+ """
210
+ return html_content
211
+
212
  def summarize_node(node_data: Dict[str, Any]) -> str:
213
  """Generate a summary for a selected node"""
214
  summary = f"""
 
319
  with gr.Accordion("Exploration Result", open=False):
320
  text_output = gr.JSON(label="Raw Result")
321
 
322
+ graph_output = gr.HTML(label="Interactive Exploration Graph")
323
  node_summary = gr.Textbox(
324
  label="Node Details",
325
  lines=5,
 
332
  outputs=[text_output, graph_output, node_summary]
333
  )
334
 
335
+ # Add examples
 
 
 
 
 
 
 
 
 
 
336
  gr.Examples(
337
+ examples=[
338
+ ["Explore the evolution of Renaissance painting techniques", "[]", "{}", 5, "Renaissance"],
339
+ ["Investigate the influence of Japanese art on Impressionism", "[]", "{}", 7, "Impressionism"],
340
+ ["Analyze the development of Cubism through Picasso's work", "[]", "{}", 6, "Cubism"]
341
+ ],
342
  inputs=[query_input, path_history, parameters, depth, domain]
343
  )
344