SorrelC commited on
Commit
e086f57
Β·
verified Β·
1 Parent(s): 66bba48

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -126
app.py CHANGED
@@ -254,101 +254,104 @@ def collect_entities_from_records(*args):
254
  - **Organizations:** {sum(1 for e in builder.entities if e['type'] == 'ORGANIZATION')}
255
  - **Dates:** {sum(1 for e in builder.entities if e['type'] == 'DATE')}
256
 
257
- Now define relationships between these entities below.
258
  """
259
 
260
- # Return summary and update dropdowns
261
- return (
262
- summary,
263
- gr.update(visible=True), # Show relationship section
264
- gr.update(choices=entity_names, value=None), # Update all relationship dropdowns
265
- gr.update(choices=entity_names, value=None),
266
- gr.update(choices=entity_names, value=None),
267
- gr.update(choices=entity_names, value=None),
268
- gr.update(choices=entity_names, value=None),
269
- gr.update(choices=entity_names, value=None),
270
- gr.update(choices=entity_names, value=None),
271
- gr.update(choices=entity_names, value=None),
272
- gr.update(choices=entity_names, value=None),
273
- gr.update(choices=entity_names, value=None)
274
- )
275
 
276
  def generate_network_graph(*args):
277
  """Generate the network graph from all inputs"""
278
- builder = NetworkGraphBuilder()
279
-
280
- # Collect entities (first 30 args: 6 records Γ— 5 fields)
281
- num_records = 6
282
- fields_per_record = 5
283
-
284
- for i in range(num_records):
285
- record_id = i + 1
286
- base_idx = i * fields_per_record
287
 
288
- person = args[base_idx] if base_idx < len(args) else ""
289
- location = args[base_idx + 1] if base_idx + 1 < len(args) else ""
290
- event = args[base_idx + 2] if base_idx + 2 < len(args) else ""
291
- org = args[base_idx + 3] if base_idx + 3 < len(args) else ""
292
- date = args[base_idx + 4] if base_idx + 4 < len(args) else ""
293
 
294
- if person:
295
- builder.add_entity(person, 'PERSON', record_id)
296
- if location:
297
- builder.add_entity(location, 'LOCATION', record_id)
298
- if event:
299
- builder.add_entity(event, 'EVENT', record_id)
300
- if org:
301
- builder.add_entity(org, 'ORGANIZATION', record_id)
302
- if date:
303
- builder.add_entity(date, 'DATE', record_id)
304
-
305
- # Collect relationships (next args: 5 relationships Γ— 3 fields)
306
- relationship_start = 30
307
- num_relationships = 5
308
-
309
- for i in range(num_relationships):
310
- base_idx = relationship_start + (i * 3)
311
- source = args[base_idx] if base_idx < len(args) else None
312
- rel_type = args[base_idx + 1] if base_idx + 1 < len(args) else None
313
- target = args[base_idx + 2] if base_idx + 2 < len(args) else None
314
-
315
- if source and target:
316
- builder.add_relationship(source, target, rel_type)
317
-
318
- # Get layout type (last arg)
319
- layout_type = args[-1] if len(args) > relationship_start else 'spring'
320
-
321
- # Build graph
322
- G = builder.build_graph()
323
-
324
- if len(G.nodes) == 0:
325
- return None, "❌ No entities to display. Please add some entities first."
326
-
327
- # Create visualization (even if no relationships, show isolated nodes)
328
- fig = builder.create_plotly_graph(G, layout_type)
329
-
330
- # Create statistics
331
- stats = f"""
332
- ### πŸ“ˆ Network Statistics
333
- - **Nodes (Entities):** {G.number_of_nodes()}
334
- - **Edges (Relationships):** {G.number_of_edges()}
335
- """
336
-
337
- if len(G.edges) == 0:
338
- stats += "\n⚠️ **No relationships defined** - showing isolated nodes only.\n"
339
- else:
340
- stats += f"- **Network Density:** {nx.density(G):.3f}\n"
341
- stats += f"- **Average Connections per Node:** {sum(dict(G.degree()).values()) / G.number_of_nodes():.2f}\n"
342
-
343
- if G.number_of_edges() > 0:
344
- # Find most connected nodes
345
- degrees = dict(G.degree())
346
- top_nodes = sorted(degrees.items(), key=lambda x: x[1], reverse=True)[:3]
347
- stats += "\n**Most Connected Entities:**\n"
348
- for node, degree in top_nodes:
349
- stats += f"- {node}: {degree} connections\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
- return fig, stats
 
 
 
 
 
 
 
 
 
 
 
352
 
353
  def create_interface():
354
  with gr.Blocks(title="Basic Network Explorer", theme=gr.themes.Soft()) as demo:
@@ -359,11 +362,11 @@ def create_interface():
359
  This tool demonstrates how NER can be used to visualize relationships and connections in text data.
360
 
361
  ### How to use this tool:
362
- 1. **πŸ“ Enter entities** in the records below (people, locations, events, organizations, dates)
363
- 2. **πŸ”— Click "Identify Entities"** to gather all your inputs
364
- 3. **🀝 Define relationships** between entities in the relationship builder
365
- 4. **🎨 Choose a layout style** and click "Generate Network Graph"
366
- 5. **πŸ‘οΈ Explore** the interactive visualization
367
  6. **πŸ”„ Refresh the page** to start over with new data
368
  """)
369
 
@@ -382,7 +385,8 @@ def create_interface():
382
  # LEFT COLUMN: Entity Inputs
383
  with gr.Column(scale=1):
384
  with gr.Accordion("πŸ“š Step 1: Enter Entities from Your Records", open=True):
385
- for i in range(6):
 
386
  with gr.Group():
387
  gr.Markdown(f"### Record {i+1}")
388
  with gr.Row():
@@ -394,42 +398,55 @@ def create_interface():
394
  date = gr.Textbox(label="πŸ—“οΈ Date", placeholder="e.g., 1940")
395
 
396
  entity_inputs.extend([person, location, event, org, date])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
 
398
  collect_btn = gr.Button("πŸ” Identify Entities", variant="primary", size="lg")
399
  entity_summary = gr.Markdown()
400
 
401
- # RIGHT COLUMN: Relationship Builder
402
  with gr.Column(scale=1):
403
- # Relationship section (initially hidden)
404
- with gr.Column(visible=False) as relationship_section:
405
- with gr.Accordion("🀝 Step 2: Define Relationships Between Entities", open=True):
406
- gr.Markdown("Select entities and specify how they're connected:")
407
 
408
- relationship_inputs = []
 
 
 
 
 
 
 
 
 
 
409
 
410
- for i in range(5):
411
- with gr.Row():
412
- source = gr.Dropdown(label=f"From", choices=[], interactive=True, scale=2)
413
- rel_type = gr.Dropdown(
414
- label="Type",
415
- choices=RELATIONSHIP_TYPES,
416
- value="related_to",
417
- interactive=True,
418
- scale=2
419
- )
420
- target = gr.Dropdown(label=f"To", choices=[], interactive=True, scale=2)
421
-
422
- relationship_inputs.extend([source, rel_type, target])
423
 
424
- with gr.Accordion("🎨 Step 3: Customize and Generate", open=True):
425
- layout_type = gr.Dropdown(
426
- label="Graph Layout",
427
- choices=['spring', 'circular', 'kamada_kawai', 'shell'],
428
- value='spring',
429
- info="Choose how nodes are arranged"
430
- )
431
-
432
- generate_btn = gr.Button("πŸ” Generate Network Graph", variant="primary", size="lg")
433
 
434
  # Output section
435
  gr.HTML("<hr style='margin: 30px 0;'>")
@@ -527,14 +544,11 @@ def create_interface():
527
  """)
528
 
529
  # Wire up the interface
530
- # Collect entities button
531
  collect_btn.click(
532
  fn=collect_entities_from_records,
533
  inputs=entity_inputs,
534
- outputs=[
535
- entity_summary,
536
- relationship_section
537
- ] + relationship_inputs[::3] + relationship_inputs[2::3] # Update source and target dropdowns
538
  )
539
 
540
  # Generate graph button
 
254
  - **Organizations:** {sum(1 for e in builder.entities if e['type'] == 'ORGANIZATION')}
255
  - **Dates:** {sum(1 for e in builder.entities if e['type'] == 'DATE')}
256
 
257
+ Now define relationships between these entities on the right β†’
258
  """
259
 
260
+ # Return summary and update all dropdowns (5 relationships Γ— 2 dropdowns each = 10 updates)
261
+ dropdown_updates = [gr.update(choices=entity_names, value=None)] * 10
262
+ return [summary] + dropdown_updates
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
  def generate_network_graph(*args):
265
  """Generate the network graph from all inputs"""
266
+ try:
267
+ builder = NetworkGraphBuilder()
 
 
 
 
 
 
 
268
 
269
+ # Collect entities (first 30 args: 6 records Γ— 5 fields)
270
+ num_records = 6
271
+ fields_per_record = 5
 
 
272
 
273
+ for i in range(num_records):
274
+ record_id = i + 1
275
+ base_idx = i * fields_per_record
276
+
277
+ person = args[base_idx] if base_idx < len(args) else ""
278
+ location = args[base_idx + 1] if base_idx + 1 < len(args) else ""
279
+ event = args[base_idx + 2] if base_idx + 2 < len(args) else ""
280
+ org = args[base_idx + 3] if base_idx + 3 < len(args) else ""
281
+ date = args[base_idx + 4] if base_idx + 4 < len(args) else ""
282
+
283
+ if person:
284
+ builder.add_entity(person, 'PERSON', record_id)
285
+ if location:
286
+ builder.add_entity(location, 'LOCATION', record_id)
287
+ if event:
288
+ builder.add_entity(event, 'EVENT', record_id)
289
+ if org:
290
+ builder.add_entity(org, 'ORGANIZATION', record_id)
291
+ if date:
292
+ builder.add_entity(date, 'DATE', record_id)
293
+
294
+ # Collect relationships (next args: 5 relationships Γ— 3 fields)
295
+ relationship_start = 30
296
+ num_relationships = 5
297
+
298
+ for i in range(num_relationships):
299
+ base_idx = relationship_start + (i * 3)
300
+ source = args[base_idx] if base_idx < len(args) else None
301
+ rel_type = args[base_idx + 1] if base_idx + 1 < len(args) else None
302
+ target = args[base_idx + 2] if base_idx + 2 < len(args) else None
303
+
304
+ if source and target:
305
+ builder.add_relationship(source, target, rel_type)
306
+
307
+ # Get layout type (last arg)
308
+ layout_type = args[-1] if len(args) > relationship_start else 'spring'
309
+
310
+ # Build graph
311
+ G = builder.build_graph()
312
+
313
+ if len(G.nodes) == 0:
314
+ return None, "❌ **No entities to display.** Please enter entities in Step 1 and click 'Identify Entities' first."
315
+
316
+ # Create visualization (even if no relationships, show isolated nodes)
317
+ fig = builder.create_plotly_graph(G, layout_type)
318
+
319
+ # Create statistics
320
+ stats = f"""
321
+ ### πŸ“ˆ Network Statistics
322
+ - **Nodes (Entities):** {G.number_of_nodes()}
323
+ - **Edges (Relationships):** {G.number_of_edges()}
324
+ """
325
+
326
+ if len(G.edges) == 0:
327
+ stats += "\n⚠️ **No relationships defined** - showing isolated nodes only.\n"
328
+ stats += "\n*Define relationships in Step 2 to see connections between entities.*\n"
329
+ else:
330
+ stats += f"- **Network Density:** {nx.density(G):.3f}\n"
331
+ stats += f"- **Average Connections per Node:** {sum(dict(G.degree()).values()) / G.number_of_nodes():.2f}\n"
332
+
333
+ if G.number_of_edges() > 0:
334
+ # Find most connected nodes
335
+ degrees = dict(G.degree())
336
+ top_nodes = sorted(degrees.items(), key=lambda x: x[1], reverse=True)[:3]
337
+ stats += "\n**Most Connected Entities:**\n"
338
+ for node, degree in top_nodes:
339
+ stats += f"- {node}: {degree} connections\n"
340
+
341
+ return fig, stats
342
 
343
+ except Exception as e:
344
+ error_msg = f"""
345
+ ### ❌ Error Generating Graph
346
+
347
+ An error occurred: {str(e)}
348
+
349
+ **Troubleshooting:**
350
+ 1. Make sure you've clicked "Identify Entities" first
351
+ 2. Check that you have at least one entity entered
352
+ 3. If problem persists, try refreshing the page
353
+ """
354
+ return None, error_msg
355
 
356
  def create_interface():
357
  with gr.Blocks(title="Basic Network Explorer", theme=gr.themes.Soft()) as demo:
 
362
  This tool demonstrates how NER can be used to visualize relationships and connections in text data.
363
 
364
  ### How to use this tool:
365
+ 1. **πŸ“ Enter entities** in the records on the left (people, locations, events, organizations, dates)
366
+ 2. **πŸ”— Click "Identify Entities"** to populate the relationship dropdowns
367
+ 3. **🀝 Define relationships** on the right by selecting entities and connection types
368
+ 4. **🎨 Click "Generate Network Graph"** to visualize your network
369
+ 5. **πŸ‘οΈ Explore** the interactive graph - hover over nodes and edges for details
370
  6. **πŸ”„ Refresh the page** to start over with new data
371
  """)
372
 
 
385
  # LEFT COLUMN: Entity Inputs
386
  with gr.Column(scale=1):
387
  with gr.Accordion("πŸ“š Step 1: Enter Entities from Your Records", open=True):
388
+ # First 4 records (always visible)
389
+ for i in range(4):
390
  with gr.Group():
391
  gr.Markdown(f"### Record {i+1}")
392
  with gr.Row():
 
398
  date = gr.Textbox(label="πŸ—“οΈ Date", placeholder="e.g., 1940")
399
 
400
  entity_inputs.extend([person, location, event, org, date])
401
+
402
+ # Additional records (collapsible)
403
+ with gr.Accordion("βž• Additional Records (5-6)", open=False):
404
+ for i in range(4, 6):
405
+ with gr.Group():
406
+ gr.Markdown(f"### Record {i+1}")
407
+ with gr.Row():
408
+ person = gr.Textbox(label="πŸ‘€ Person", placeholder="e.g., Winston Churchill")
409
+ location = gr.Textbox(label="πŸ“ Location", placeholder="e.g., London")
410
+ with gr.Row():
411
+ event = gr.Textbox(label="πŸ“… Event", placeholder="e.g., Battle of Britain")
412
+ org = gr.Textbox(label="🏒 Organization", placeholder="e.g., Royal Air Force")
413
+ date = gr.Textbox(label="πŸ—“οΈ Date", placeholder="e.g., 1940")
414
+
415
+ entity_inputs.extend([person, location, event, org, date])
416
 
417
  collect_btn = gr.Button("πŸ” Identify Entities", variant="primary", size="lg")
418
  entity_summary = gr.Markdown()
419
 
420
+ # RIGHT COLUMN: Relationship Builder (ALWAYS VISIBLE)
421
  with gr.Column(scale=1):
422
+ with gr.Accordion("🀝 Step 2: Define Relationships Between Entities", open=True):
423
+ gr.Markdown("*First identify entities, then define relationships below:*")
424
+
425
+ relationship_inputs = []
426
 
427
+ for i in range(5):
428
+ with gr.Row():
429
+ source = gr.Dropdown(label=f"From", choices=[], interactive=True, scale=2)
430
+ rel_type = gr.Dropdown(
431
+ label="Type",
432
+ choices=RELATIONSHIP_TYPES,
433
+ value="related_to",
434
+ interactive=True,
435
+ scale=2
436
+ )
437
+ target = gr.Dropdown(label=f"To", choices=[], interactive=True, scale=2)
438
 
439
+ relationship_inputs.extend([source, rel_type, target])
440
+
441
+ with gr.Accordion("🎨 Step 3: Customize and Generate", open=True):
442
+ layout_type = gr.Dropdown(
443
+ label="Graph Layout",
444
+ choices=['spring', 'circular', 'kamada_kawai', 'shell'],
445
+ value='spring',
446
+ info="Choose how nodes are arranged"
447
+ )
 
 
 
 
448
 
449
+ generate_btn = gr.Button("πŸ” Generate Network Graph", variant="primary", size="lg")
 
 
 
 
 
 
 
 
450
 
451
  # Output section
452
  gr.HTML("<hr style='margin: 30px 0;'>")
 
544
  """)
545
 
546
  # Wire up the interface
547
+ # Collect entities button - updates the relationship dropdowns
548
  collect_btn.click(
549
  fn=collect_entities_from_records,
550
  inputs=entity_inputs,
551
+ outputs=[entity_summary] + relationship_inputs[::3] + relationship_inputs[2::3] # Update source and target dropdowns
 
 
 
552
  )
553
 
554
  # Generate graph button