-
Notifications
You must be signed in to change notification settings - Fork 1
/
view_wikipedia.py
146 lines (114 loc) · 3.94 KB
/
view_wikipedia.py
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
import random
import math
from enum import Enum
from pygraphvis import *
from urllib.request import urlopen
from threading import *
SPAWN_DIST = 7
DO_RECONNECT_EDGES = False
class NodeState(Enum):
UNFETCHED = 1
FETCHING = 2
FETCHED = 3
class NodePrivateData():
state = NodeState.UNFETCHED
unrevealed = None
real_degree = 0
manually_static = False
def __init__(self):
self.unrevealed = []
def update(self, node):
if self.state == NodeState.UNFETCHED:
self.state = NodeState.FETCHING
node.style.value.font_colour = (255, 255, 0)
node.style.invalidate()
crawler = Thread(target = crawl_page, args = (node, ))
crawler.daemon = True
crawler.start()
elif self.state == NodeState.FETCHED:
self.reveal_one(node)
def reveal_one(self, node):
if len(self.unrevealed) == 0:
return
child_name = self.unrevealed.pop()
v.lock.acquire()
child = None
for n in v.graph.nodes:
if n.style.value.name == child_name:
child = n
if not DO_RECONNECT_EDGES:
v.lock.release()
self.reveal_one(node)
return
break
if child == None:
child = create_new_node(node.pos, child_name)
if not child in node.adj:
node.adj[child] = None
if not node in child.adj:
child.adj[node] = None
v.lock.release()
# Called under v.lock()
def create_new_node(parent_pos, name, random_off = True):
spawn_dist = SPAWN_DIST if random_off else 0
angle = random.uniform(0, 2 * math.pi)
pos = vec.add(parent_pos, vec.rotate2d((spawn_dist, 0), angle))
n = Node(name = name, pos = pos, colour = (100, 100, 100))
n.private = NodePrivateData()
v.graph.nodes.add(n)
return n
def set_node_style(node):
score = float(node.private.real_degree) / highest_degree
node.style.value.radius = int(20 * score + 8)
if node.private.manually_static:
colour = (0, 255, 0)
else:
colour = vec.int_round(vec.mul((score, 0, 1 - score), 255))
node.style.value.colour = colour
def restyle_nodes():
v.lock.acquire()
for node in v.graph.nodes:
if node.private.state == NodeState.FETCHED:
set_node_style(node)
v.lock.release()
BANNED_CHARS = [":", "#", "%"]
def find_wiki_links(page):
pages = set()
html = urlopen("http://en.wikipedia.org/wiki/" + page).read()
for str in html.decode('utf-8').split("\""):
if str.startswith("/wiki/") and not any([c in str for c in BANNED_CHARS]):
pages.add(str[6:])
return list(pages)
def crawl_page(node):
links = find_wiki_links(node.style.value.name)
node.style.value.font_colour = (255, 255, 255)
node.style.invalidate()
node.private.state = NodeState.FETCHED
node.private.unrevealed = links
node.private.real_degree = len(links)
global highest_degree
highest_degree = max(highest_degree, len(links))
for i in range(0, 5):
node.private.reveal_one(node)
restyle_nodes()
def event_handler(e):
if e.type == InputType.QUIT:
v.stop()
elif e.type == InputType.M_MOVE:
return
node = v.get_mousedover_node()
if node == None:
return
if e.type == InputType.MB_RIGHT and e.state == MouseState.UP:
node.private.update(node)
elif e.type == InputType.MB_MIDDLE and e.state == MouseState.UP:
node.private.manually_static = not node.private.manually_static
node.static = node.private.manually_static
set_node_style(node)
if __name__ == "__main__":
highest_degree = 1
v = vis.Visualiser(graphs.DynamicGraph(), size = (1200, 1000),
event_handler = event_handler)
root_name = input("Please enter the name of a Wikipedia page: ")
create_new_node((0, 0), root_name, False)
v.render_loop()