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
|
#!/usr/bin/env python2.4
import sys
from subprocess import Popen, PIPE
import cgi
import cgitb; cgitb.enable()
from urllib import urlencode
from xml.sax.saxutils import escape
import os.path
REQ_DIR = '/home/abentley/pastegraph'
class Node(object):
def __init__(self, name, color=None):
self.name = name
self.color = color
def define(self):
if self.color is not None:
return '%s[fillcolor="%s" style="filled"]' % \
(self.name, self.color)
def __str__(self):
return self.name
def save_request(req):
files = [int(f) for f in os.listdir(REQ_DIR)]
files.sort()
if len(files) == 0:
stem = '0'
else:
stem = str(files[-1]+1)
filename = os.path.join(REQ_DIR, stem)
file(filename, 'wb').write(req)
return stem
def load_request(num):
return file(os.path.join(REQ_DIR, num), 'rb').read()
def parsed_link(link):
suffixes = {'$': "#ffee99",
'!': "#ff0000",
'\'': "#eeeeee",
'?': "#7799ff",
}
if link[-1] in suffixes:
return link[:-1], suffixes[link[-1]]
else:
return link, None
def parse(input):
input = input.replace('\n', ' ')
input = input.replace('\r', ' ')
statements = input.split(' ')
relations = []
nodes = {}
for statement in statements:
if len(statement.strip(' ')) == 0:
continue
links = statement.split('->')
new_links = []
for link, color in (parsed_link(l) for l in links):
if link not in nodes:
nodes[link] = Node(link, color)
elif color is not None:
nodes[link].color = color
new_links.append(link)
for i in range(len(links)-1):
relations.append((nodes[new_links[i]], nodes[new_links[i+1]]))
return relations
def dot_output(my_file, relations):
defined = set()
yield "digraph G\n"
yield "{\n"
for (start, end) in relations:
if start.name not in defined:
defined.add(start.name)
my_def = start.define()
if my_def is not None:
yield " %s;\n" % my_def
if end.name not in defined:
defined.add(end.name)
my_def = end.define()
if my_def is not None:
yield " %s;\n" % my_def
yield " %s->%s;\n" % (start.name, end.name)
yield "}\n"
def invoke_dot(input, out_file=None):
cmdline = ['dot', '-Tgif']
if out_file is not None:
cmdline.extend(('-o', out_file))
dot_proc = Popen(cmdline, stdin=PIPE)
for line in input:
dot_proc.stdin.write(line)
dot_proc.stdin.close()
return dot_proc.wait()
def do_form(seqnum):
if seqnum is not None:
input = load_request(seqnum)
else:
input = "A->B B->C A->C$"
print "Content-type: text/html;charset=utf-8"
print ""
if input is not None:
img_src = "?" + urlencode({"graphtext": input, "type": "img"})
img = '<img src="%s" alt="result graph"/>' % escape(img_src)
dotcode = ''.join(list(dot_output(sys.stdout, parse(input))))
print """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/2002/REC-xhtml1-20020801/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Pastegraph</title>
</head>
<body>
<p>
This is a quikie tool for directed acyclic graphs.
It uses a simple 'A->B' notation to describe the graphs.
</p>
<p>
You can use the suffixes !, ', $ and ? to emphasize nodes. E.g. A$->B
will cause A to be yellow.
</p>
<p>
Yes, that's Dot under the covers.
</p>
<form method="post" action="">
<p><textarea name="graphtext" rows="5" cols="40">%s</textarea></p>
<p><input type="submit" value="Draw graph"/></p>
<p><input type="hidden" name="type" value="save"/></p>
</form>
<div>%s</div>
<pre>%s</pre>
</body>
</html>""" % (input, img, escape(dotcode))
def save_redirect(input):
seqnum = save_request(input)
print "Status: 302 Found"
print "Location: ?%s" % urlencode({"n": seqnum})
def main():
form = cgi.FieldStorage()
type = form.getfirst("type", "form")
input = form.getfirst("graphtext", "A->B B->C A->C$")
seqnum = form.getfirst("n", None)
if type == "form":
do_form(seqnum)
elif type == "save":
save_redirect(input)
else:
print "Content-type: image/gif\n"
sys.stdout.flush()
invoke_dot(dot_output(sys.stdout, parse(input)))
main()
|