Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,11 +3,12 @@ import networkx as nx
|
|
| 3 |
from pyvis.network import Network
|
| 4 |
import base64
|
| 5 |
|
| 6 |
-
# Entity type
|
| 7 |
-
|
|
|
|
| 8 |
'PERSON': '#00B894', # Green
|
| 9 |
-
'LOCATION': '#
|
| 10 |
-
'EVENT': '#
|
| 11 |
'ORGANIZATION': '#55A3FF', # Light Blue
|
| 12 |
'DATE': '#FF6B6B' # Red
|
| 13 |
}
|
|
@@ -87,7 +88,7 @@ class NetworkGraphBuilder:
|
|
| 87 |
return G
|
| 88 |
|
| 89 |
def create_pyvis_graph(self, G):
|
| 90 |
-
"""Create interactive PyVis
|
| 91 |
if len(G.nodes) == 0:
|
| 92 |
return None
|
| 93 |
|
|
@@ -147,7 +148,7 @@ class NetworkGraphBuilder:
|
|
| 147 |
"springConstant": 0.04,
|
| 148 |
"damping": 0.09
|
| 149 |
},
|
| 150 |
-
"
|
| 151 |
"enabled": true,
|
| 152 |
"iterations": 100
|
| 153 |
}
|
|
@@ -166,7 +167,7 @@ class NetworkGraphBuilder:
|
|
| 166 |
for node in G.nodes():
|
| 167 |
data = G.nodes[node]
|
| 168 |
entity_type = data.get('entity_type', 'UNKNOWN')
|
| 169 |
-
|
| 170 |
|
| 171 |
# Size based on connections (degree)
|
| 172 |
degree = G.degree(node)
|
|
@@ -190,7 +191,7 @@ class NetworkGraphBuilder:
|
|
| 190 |
net.add_node(
|
| 191 |
node,
|
| 192 |
label=node,
|
| 193 |
-
color=
|
| 194 |
size=size,
|
| 195 |
title=title,
|
| 196 |
font={'size': 18, 'color': 'white', 'strokeWidth': 3, 'strokeColor': '#1a1a2e'}
|
|
@@ -297,7 +298,7 @@ def collect_entities_from_records(
|
|
| 297 |
</div>
|
| 298 |
<div style="background: rgba(255,255,255,0.2); padding: 10px 20px; border-radius: 8px; text-align: center;">
|
| 299 |
<div style="font-size: 24px; font-weight: bold; color: white;">{counts['ORGANIZATION']}</div>
|
| 300 |
-
<div style="color: rgba(255,255,255,0.9); font-size: 12px;">π’
|
| 301 |
</div>
|
| 302 |
<div style="background: rgba(255,255,255,0.2); padding: 10px 20px; border-radius: 8px; text-align: center;">
|
| 303 |
<div style="font-size: 24px; font-weight: bold; color: white;">{counts['DATE']}</div>
|
|
@@ -399,7 +400,7 @@ def generate_network_graph(
|
|
| 399 |
'''
|
| 400 |
return empty_html, "β **No entities to display.** Please enter entities in Step 1 first."
|
| 401 |
|
| 402 |
-
# Create
|
| 403 |
graph_html = builder.create_pyvis_graph(G)
|
| 404 |
|
| 405 |
# Create statistics
|
|
@@ -515,6 +516,22 @@ def load_wwii_example():
|
|
| 515 |
)
|
| 516 |
|
| 517 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 518 |
def create_interface():
|
| 519 |
with gr.Blocks(title="Network Explorer", theme=gr.themes.Soft()) as demo:
|
| 520 |
gr.Markdown("""
|
|
@@ -526,7 +543,7 @@ def create_interface():
|
|
| 526 |
1. **π Enter entities** in the records below (or load an example to get started)
|
| 527 |
2. **π Click "Identify Entities"** to collect and list all entities
|
| 528 |
3. **π€ Define relationships** between entities using the dropdowns
|
| 529 |
-
4. **π¨ Click "Generate Network Graph"** to
|
| 530 |
5. **ποΈ Explore** - drag nodes to rearrange, scroll to zoom, hover for details
|
| 531 |
""")
|
| 532 |
|
|
@@ -553,38 +570,58 @@ def create_interface():
|
|
| 553 |
with gr.Row():
|
| 554 |
with gr.Column(scale=1, min_width=200):
|
| 555 |
gr.Markdown("**Record 1**")
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
entity_inputs.extend([p1, l1, e1, o1, d1])
|
| 562 |
|
| 563 |
with gr.Column(scale=1, min_width=200):
|
| 564 |
gr.Markdown("**Record 2**")
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 570 |
entity_inputs.extend([p2, l2, e2, o2, d2])
|
| 571 |
|
| 572 |
with gr.Column(scale=1, min_width=200):
|
| 573 |
gr.Markdown("**Record 3**")
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
entity_inputs.extend([p3, l3, e3, o3, d3])
|
| 580 |
|
| 581 |
with gr.Column(scale=1, min_width=200):
|
| 582 |
gr.Markdown("**Record 4**")
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 588 |
entity_inputs.extend([p4, l4, e4, o4, d4])
|
| 589 |
|
| 590 |
# Additional records 5-8
|
|
@@ -592,38 +629,58 @@ def create_interface():
|
|
| 592 |
with gr.Row():
|
| 593 |
with gr.Column(scale=1, min_width=200):
|
| 594 |
gr.Markdown("**Record 5**")
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 600 |
entity_inputs.extend([p5, l5, e5, o5, d5])
|
| 601 |
|
| 602 |
with gr.Column(scale=1, min_width=200):
|
| 603 |
gr.Markdown("**Record 6**")
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 609 |
entity_inputs.extend([p6, l6, e6, o6, d6])
|
| 610 |
|
| 611 |
with gr.Column(scale=1, min_width=200):
|
| 612 |
gr.Markdown("**Record 7**")
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 618 |
entity_inputs.extend([p7, l7, e7, o7, d7])
|
| 619 |
|
| 620 |
with gr.Column(scale=1, min_width=200):
|
| 621 |
gr.Markdown("**Record 8**")
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 627 |
entity_inputs.extend([p8, l8, e8, o8, d8])
|
| 628 |
|
| 629 |
# Identify button
|
|
@@ -691,29 +748,29 @@ def create_interface():
|
|
| 691 |
with gr.Column(scale=1):
|
| 692 |
network_stats = gr.HTML()
|
| 693 |
|
| 694 |
-
#
|
| 695 |
-
gr.HTML("""
|
| 696 |
<div style="background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); padding: 20px; border-radius: 10px; margin-top: 20px;">
|
| 697 |
-
<h4 style="color: white; margin: 0 0 15px 0;">π¨ Entity
|
| 698 |
<div style="display: flex; flex-wrap: wrap; gap: 20px;">
|
| 699 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 700 |
-
<span style="width: 20px; height: 20px; border-radius: 50%; background-color:
|
| 701 |
Person
|
| 702 |
</span>
|
| 703 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 704 |
-
<span style="width: 20px; height: 20px; border-radius: 50%; background-color:
|
| 705 |
Location
|
| 706 |
</span>
|
| 707 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 708 |
-
<span style="width: 20px; height: 20px; border-radius: 50%; background-color:
|
| 709 |
Event
|
| 710 |
</span>
|
| 711 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 712 |
-
<span style="width: 20px; height: 20px; border-radius: 50%; background-color:
|
| 713 |
-
|
| 714 |
</span>
|
| 715 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 716 |
-
<span style="width: 20px; height: 20px; border-radius: 50%; background-color:
|
| 717 |
Date
|
| 718 |
</span>
|
| 719 |
</div>
|
|
@@ -760,9 +817,35 @@ def create_interface():
|
|
| 760 |
outputs=[network_plot, network_stats]
|
| 761 |
)
|
| 762 |
|
| 763 |
-
#
|
| 764 |
gr.HTML("""
|
| 765 |
<hr style="margin: 40px 0 20px 0;">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 766 |
<div style="text-align: center; color: #666; font-size: 14px; padding: 20px;">
|
| 767 |
<p>This <strong>Basic Network Explorer</strong> tool was developed as part of a Bodleian Libraries (Oxford) Sassoon Research Fellowship.</p>
|
| 768 |
<p style="color: #888; font-size: 12px; margin-top: 10px;">Built with the aid of Claude Opus 4.5.</p>
|
|
|
|
| 3 |
from pyvis.network import Network
|
| 4 |
import base64
|
| 5 |
|
| 6 |
+
# Entity type colours (matching your NER tool)
|
| 7 |
+
# Updated for better distinction between Person, Location, and Event
|
| 8 |
+
ENTITY_COLOURS = {
|
| 9 |
'PERSON': '#00B894', # Green
|
| 10 |
+
'LOCATION': '#9B59B6', # Purple (more distinct from green)
|
| 11 |
+
'EVENT': '#F39C12', # Orange/Gold (very distinct)
|
| 12 |
'ORGANIZATION': '#55A3FF', # Light Blue
|
| 13 |
'DATE': '#FF6B6B' # Red
|
| 14 |
}
|
|
|
|
| 88 |
return G
|
| 89 |
|
| 90 |
def create_pyvis_graph(self, G):
|
| 91 |
+
"""Create interactive PyVis visualisation"""
|
| 92 |
if len(G.nodes) == 0:
|
| 93 |
return None
|
| 94 |
|
|
|
|
| 148 |
"springConstant": 0.04,
|
| 149 |
"damping": 0.09
|
| 150 |
},
|
| 151 |
+
"stabilisation": {
|
| 152 |
"enabled": true,
|
| 153 |
"iterations": 100
|
| 154 |
}
|
|
|
|
| 167 |
for node in G.nodes():
|
| 168 |
data = G.nodes[node]
|
| 169 |
entity_type = data.get('entity_type', 'UNKNOWN')
|
| 170 |
+
colour = ENTITY_COLOURS.get(entity_type, '#CCCCCC')
|
| 171 |
|
| 172 |
# Size based on connections (degree)
|
| 173 |
degree = G.degree(node)
|
|
|
|
| 191 |
net.add_node(
|
| 192 |
node,
|
| 193 |
label=node,
|
| 194 |
+
color=colour,
|
| 195 |
size=size,
|
| 196 |
title=title,
|
| 197 |
font={'size': 18, 'color': 'white', 'strokeWidth': 3, 'strokeColor': '#1a1a2e'}
|
|
|
|
| 298 |
</div>
|
| 299 |
<div style="background: rgba(255,255,255,0.2); padding: 10px 20px; border-radius: 8px; text-align: center;">
|
| 300 |
<div style="font-size: 24px; font-weight: bold; color: white;">{counts['ORGANIZATION']}</div>
|
| 301 |
+
<div style="color: rgba(255,255,255,0.9); font-size: 12px;">π’ Organisations</div>
|
| 302 |
</div>
|
| 303 |
<div style="background: rgba(255,255,255,0.2); padding: 10px 20px; border-radius: 8px; text-align: center;">
|
| 304 |
<div style="font-size: 24px; font-weight: bold; color: white;">{counts['DATE']}</div>
|
|
|
|
| 400 |
'''
|
| 401 |
return empty_html, "β **No entities to display.** Please enter entities in Step 1 first."
|
| 402 |
|
| 403 |
+
# Create visualisation
|
| 404 |
graph_html = builder.create_pyvis_graph(G)
|
| 405 |
|
| 406 |
# Create statistics
|
|
|
|
| 516 |
)
|
| 517 |
|
| 518 |
|
| 519 |
+
def create_coloured_label(text, colour, emoji):
|
| 520 |
+
"""Create a coloured pill-style label HTML"""
|
| 521 |
+
return f'''
|
| 522 |
+
<span style="
|
| 523 |
+
background-color: {colour};
|
| 524 |
+
color: white;
|
| 525 |
+
padding: 4px 12px;
|
| 526 |
+
border-radius: 15px;
|
| 527 |
+
font-size: 13px;
|
| 528 |
+
font-weight: 500;
|
| 529 |
+
display: inline-block;
|
| 530 |
+
margin-bottom: 5px;
|
| 531 |
+
">{emoji} {text}</span>
|
| 532 |
+
'''
|
| 533 |
+
|
| 534 |
+
|
| 535 |
def create_interface():
|
| 536 |
with gr.Blocks(title="Network Explorer", theme=gr.themes.Soft()) as demo:
|
| 537 |
gr.Markdown("""
|
|
|
|
| 543 |
1. **π Enter entities** in the records below (or load an example to get started)
|
| 544 |
2. **π Click "Identify Entities"** to collect and list all entities
|
| 545 |
3. **π€ Define relationships** between entities using the dropdowns
|
| 546 |
+
4. **π¨ Click "Generate Network Graph"** to visualise
|
| 547 |
5. **ποΈ Explore** - drag nodes to rearrange, scroll to zoom, hover for details
|
| 548 |
""")
|
| 549 |
|
|
|
|
| 570 |
with gr.Row():
|
| 571 |
with gr.Column(scale=1, min_width=200):
|
| 572 |
gr.Markdown("**Record 1**")
|
| 573 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 574 |
+
p1 = gr.Textbox(label="", placeholder="e.g., Elizabeth Bennet", show_label=False)
|
| 575 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 576 |
+
l1 = gr.Textbox(label="", placeholder="e.g., Longbourn", show_label=False)
|
| 577 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 578 |
+
e1 = gr.Textbox(label="", placeholder="e.g., Meryton Ball", show_label=False)
|
| 579 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 580 |
+
o1 = gr.Textbox(label="", placeholder="e.g., Bennet Family", show_label=False)
|
| 581 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 582 |
+
d1 = gr.Textbox(label="", placeholder="e.g., 1811", show_label=False)
|
| 583 |
entity_inputs.extend([p1, l1, e1, o1, d1])
|
| 584 |
|
| 585 |
with gr.Column(scale=1, min_width=200):
|
| 586 |
gr.Markdown("**Record 2**")
|
| 587 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 588 |
+
p2 = gr.Textbox(label="", placeholder="e.g., Mr. Darcy", show_label=False)
|
| 589 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 590 |
+
l2 = gr.Textbox(label="", placeholder="e.g., Pemberley", show_label=False)
|
| 591 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 592 |
+
e2 = gr.Textbox(label="", placeholder="e.g., Netherfield Ball", show_label=False)
|
| 593 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 594 |
+
o2 = gr.Textbox(label="", placeholder="e.g., Darcy Estate", show_label=False)
|
| 595 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 596 |
+
d2 = gr.Textbox(label="", placeholder="", show_label=False)
|
| 597 |
entity_inputs.extend([p2, l2, e2, o2, d2])
|
| 598 |
|
| 599 |
with gr.Column(scale=1, min_width=200):
|
| 600 |
gr.Markdown("**Record 3**")
|
| 601 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 602 |
+
p3 = gr.Textbox(label="", placeholder="e.g., Jane Bennet", show_label=False)
|
| 603 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 604 |
+
l3 = gr.Textbox(label="", placeholder="e.g., Netherfield", show_label=False)
|
| 605 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 606 |
+
e3 = gr.Textbox(label="", placeholder="", show_label=False)
|
| 607 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 608 |
+
o3 = gr.Textbox(label="", placeholder="", show_label=False)
|
| 609 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 610 |
+
d3 = gr.Textbox(label="", placeholder="", show_label=False)
|
| 611 |
entity_inputs.extend([p3, l3, e3, o3, d3])
|
| 612 |
|
| 613 |
with gr.Column(scale=1, min_width=200):
|
| 614 |
gr.Markdown("**Record 4**")
|
| 615 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 616 |
+
p4 = gr.Textbox(label="", placeholder="e.g., Mr. Bingley", show_label=False)
|
| 617 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 618 |
+
l4 = gr.Textbox(label="", placeholder="e.g., London", show_label=False)
|
| 619 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 620 |
+
e4 = gr.Textbox(label="", placeholder="", show_label=False)
|
| 621 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 622 |
+
o4 = gr.Textbox(label="", placeholder="", show_label=False)
|
| 623 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 624 |
+
d4 = gr.Textbox(label="", placeholder="", show_label=False)
|
| 625 |
entity_inputs.extend([p4, l4, e4, o4, d4])
|
| 626 |
|
| 627 |
# Additional records 5-8
|
|
|
|
| 629 |
with gr.Row():
|
| 630 |
with gr.Column(scale=1, min_width=200):
|
| 631 |
gr.Markdown("**Record 5**")
|
| 632 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 633 |
+
p5 = gr.Textbox(label="", show_label=False)
|
| 634 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 635 |
+
l5 = gr.Textbox(label="", show_label=False)
|
| 636 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 637 |
+
e5 = gr.Textbox(label="", show_label=False)
|
| 638 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 639 |
+
o5 = gr.Textbox(label="", show_label=False)
|
| 640 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 641 |
+
d5 = gr.Textbox(label="", show_label=False)
|
| 642 |
entity_inputs.extend([p5, l5, e5, o5, d5])
|
| 643 |
|
| 644 |
with gr.Column(scale=1, min_width=200):
|
| 645 |
gr.Markdown("**Record 6**")
|
| 646 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 647 |
+
p6 = gr.Textbox(label="", show_label=False)
|
| 648 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 649 |
+
l6 = gr.Textbox(label="", show_label=False)
|
| 650 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 651 |
+
e6 = gr.Textbox(label="", show_label=False)
|
| 652 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 653 |
+
o6 = gr.Textbox(label="", show_label=False)
|
| 654 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 655 |
+
d6 = gr.Textbox(label="", show_label=False)
|
| 656 |
entity_inputs.extend([p6, l6, e6, o6, d6])
|
| 657 |
|
| 658 |
with gr.Column(scale=1, min_width=200):
|
| 659 |
gr.Markdown("**Record 7**")
|
| 660 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 661 |
+
p7 = gr.Textbox(label="", show_label=False)
|
| 662 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 663 |
+
l7 = gr.Textbox(label="", show_label=False)
|
| 664 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 665 |
+
e7 = gr.Textbox(label="", show_label=False)
|
| 666 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 667 |
+
o7 = gr.Textbox(label="", show_label=False)
|
| 668 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 669 |
+
d7 = gr.Textbox(label="", show_label=False)
|
| 670 |
entity_inputs.extend([p7, l7, e7, o7, d7])
|
| 671 |
|
| 672 |
with gr.Column(scale=1, min_width=200):
|
| 673 |
gr.Markdown("**Record 8**")
|
| 674 |
+
gr.HTML(create_coloured_label("Person", ENTITY_COLOURS['PERSON'], "π€"))
|
| 675 |
+
p8 = gr.Textbox(label="", show_label=False)
|
| 676 |
+
gr.HTML(create_coloured_label("Location", ENTITY_COLOURS['LOCATION'], "π"))
|
| 677 |
+
l8 = gr.Textbox(label="", show_label=False)
|
| 678 |
+
gr.HTML(create_coloured_label("Event", ENTITY_COLOURS['EVENT'], "π
"))
|
| 679 |
+
e8 = gr.Textbox(label="", show_label=False)
|
| 680 |
+
gr.HTML(create_coloured_label("Organisation", ENTITY_COLOURS['ORGANIZATION'], "π’"))
|
| 681 |
+
o8 = gr.Textbox(label="", show_label=False)
|
| 682 |
+
gr.HTML(create_coloured_label("Date", ENTITY_COLOURS['DATE'], "ποΈ"))
|
| 683 |
+
d8 = gr.Textbox(label="", show_label=False)
|
| 684 |
entity_inputs.extend([p8, l8, e8, o8, d8])
|
| 685 |
|
| 686 |
# Identify button
|
|
|
|
| 748 |
with gr.Column(scale=1):
|
| 749 |
network_stats = gr.HTML()
|
| 750 |
|
| 751 |
+
# Colour legend
|
| 752 |
+
gr.HTML(f"""
|
| 753 |
<div style="background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); padding: 20px; border-radius: 10px; margin-top: 20px;">
|
| 754 |
+
<h4 style="color: white; margin: 0 0 15px 0;">π¨ Entity Colour Legend</h4>
|
| 755 |
<div style="display: flex; flex-wrap: wrap; gap: 20px;">
|
| 756 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 757 |
+
<span style="width: 20px; height: 20px; border-radius: 50%; background-color: {ENTITY_COLOURS['PERSON']}; display: inline-block; border: 2px solid white;"></span>
|
| 758 |
Person
|
| 759 |
</span>
|
| 760 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 761 |
+
<span style="width: 20px; height: 20px; border-radius: 50%; background-color: {ENTITY_COLOURS['LOCATION']}; display: inline-block; border: 2px solid white;"></span>
|
| 762 |
Location
|
| 763 |
</span>
|
| 764 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 765 |
+
<span style="width: 20px; height: 20px; border-radius: 50%; background-color: {ENTITY_COLOURS['EVENT']}; display: inline-block; border: 2px solid white;"></span>
|
| 766 |
Event
|
| 767 |
</span>
|
| 768 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 769 |
+
<span style="width: 20px; height: 20px; border-radius: 50%; background-color: {ENTITY_COLOURS['ORGANIZATION']}; display: inline-block; border: 2px solid white;"></span>
|
| 770 |
+
Organisation
|
| 771 |
</span>
|
| 772 |
<span style="display: flex; align-items: center; gap: 8px; color: white;">
|
| 773 |
+
<span style="width: 20px; height: 20px; border-radius: 50%; background-color: {ENTITY_COLOURS['DATE']}; display: inline-block; border: 2px solid white;"></span>
|
| 774 |
Date
|
| 775 |
</span>
|
| 776 |
</div>
|
|
|
|
| 817 |
outputs=[network_plot, network_stats]
|
| 818 |
)
|
| 819 |
|
| 820 |
+
# Model Information & Documentation section
|
| 821 |
gr.HTML("""
|
| 822 |
<hr style="margin: 40px 0 20px 0;">
|
| 823 |
+
<div style="background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 20px; margin: 20px 0;">
|
| 824 |
+
<h3 style="margin: 0 0 10px 0;">π Library Information & Documentation</h3>
|
| 825 |
+
<p style="color: #666; margin-bottom: 15px;">Learn more about the libraries used in this tool:</p>
|
| 826 |
+
<ul style="list-style-type: circle; padding-left: 20px; margin: 0;">
|
| 827 |
+
<li style="margin-bottom: 8px;">
|
| 828 |
+
<strong>NetworkX:</strong>
|
| 829 |
+
<a href="https://networkx.org/documentation/stable/" target="_blank" style="color: #667eea;">NetworkX Documentation β</a>
|
| 830 |
+
<span style="color: #888; font-size: 13px;"> β Python library for creating, manipulating, and studying complex networks</span>
|
| 831 |
+
</li>
|
| 832 |
+
<li style="margin-bottom: 8px;">
|
| 833 |
+
<strong>PyVis:</strong>
|
| 834 |
+
<a href="https://pyvis.readthedocs.io/en/latest/" target="_blank" style="color: #667eea;">PyVis Documentation β</a>
|
| 835 |
+
<span style="color: #888; font-size: 13px;"> β Interactive network visualisation library built on vis.js</span>
|
| 836 |
+
</li>
|
| 837 |
+
<li style="margin-bottom: 8px;">
|
| 838 |
+
<strong>Gradio:</strong>
|
| 839 |
+
<a href="https://www.gradio.app/docs" target="_blank" style="color: #667eea;">Gradio Documentation β</a>
|
| 840 |
+
<span style="color: #888; font-size: 13px;"> β Web interface framework for machine learning demos</span>
|
| 841 |
+
</li>
|
| 842 |
+
</ul>
|
| 843 |
+
</div>
|
| 844 |
+
""")
|
| 845 |
+
|
| 846 |
+
# Footer
|
| 847 |
+
gr.HTML("""
|
| 848 |
+
<hr style="margin: 20px 0 20px 0;">
|
| 849 |
<div style="text-align: center; color: #666; font-size: 14px; padding: 20px;">
|
| 850 |
<p>This <strong>Basic Network Explorer</strong> tool was developed as part of a Bodleian Libraries (Oxford) Sassoon Research Fellowship.</p>
|
| 851 |
<p style="color: #888; font-size: 12px; margin-top: 10px;">Built with the aid of Claude Opus 4.5.</p>
|