Mercurial > hg > svgsitemap
annotate svgsitemap/middleware.py @ 26:ccd73b01ba79 default tip
py3
author | Jeff Hammel <k0scist@gmail.com> |
---|---|
date | Tue, 03 Nov 2020 10:00:32 -0800 |
parents | 60baa5252aa4 |
children |
rev | line source |
---|---|
0 | 1 """ |
2 request dispatcher | |
3 """ | |
4 | |
5 __all__ = ['MapserverMiddleware', 'SVGSiteMap'] | |
6 | |
7 import os | |
1 | 8 import urlparse |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
9 from fnmatch import fnmatch |
0 | 10 from pygraphviz import AGraph |
1 | 11 from webob import Request, Response, exc |
0 | 12 |
13 class MapserverMiddleware(object): | |
14 """silly middleware to serve just the svg""" | |
15 def __init__(self, app, svgmap, path='/map'): | |
1 | 16 self.app = app |
0 | 17 self.svgmap = svgmap |
18 self.path = path | |
19 | |
20 def __call__(self, environ, start_response): | |
21 request = Request(environ) | |
22 if request.path_info == self.path or not self.path: | |
1 | 23 if not os.path.exists(self.svgmap): |
24 res = exc.HTTPNotFound() | |
25 return res(environ, start_response) | |
0 | 26 content = file(self.svgmap).read() |
1 | 27 res = Response(content_type='image/svg+xml', body=content) |
28 return res(environ, start_response) | |
29 return self.app(environ, start_response) | |
0 | 30 |
31 | |
32 class SVGSiteMap(object): | |
33 | |
34 ### class level variables | |
1 | 35 defaults = { 'name': '', |
36 'hosts': '', | |
37 'external_referers': True, | |
2 | 38 'maxwidth': 5, |
11
1ff7b363e89e
hike up minimum edge width a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
10
diff
changeset
|
39 'minwidth': '0.1', |
15
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
40 'maxlength': 60, |
8
1d16bde33eee
take out some image too ::sigh::
Jeff Hammel <jhammel@mozilla.com>
parents:
7
diff
changeset
|
41 'excluded': '*.css *.js */static/* /css/* *.ico /backgrounds/* *.png *.jpg', |
1 | 42 |
43 # input/output | |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
44 'file': None, # graphviz file |
1 | 45 'output': None, # .svg file |
46 | |
47 # graph attributes | |
0 | 48 'bgcolor': 'black', |
49 'fontcolor': 'white', | |
50 'fontname': 'Helvetica', | |
18
2aeac6508dc3
reset fontsize to something sane
Jeff Hammel <jhammel@mozilla.com>
parents:
17
diff
changeset
|
51 'fontsize': '10.0', |
0 | 52 'nodecolor': 'aqua', |
53 'edgecolor': 'lime', | |
5 | 54 'shape': 'plaintext', |
15
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
55 'len': '1.0', |
1 | 56 'arrowsize': '0.5', |
0 | 57 } |
58 | |
59 def __init__(self, app, **kw): | |
1 | 60 |
61 # boilerplate | |
0 | 62 self.app = app |
63 self.edges = {} | |
1 | 64 self.max = 0 |
65 | |
66 # set attrs from defaults | |
0 | 67 for key in self.defaults: |
68 setattr(self, key, kw.get(key, self.defaults[key])) | |
1 | 69 |
70 # sanity checks + data fixing | |
0 | 71 assert self.output, "Please give an output file" |
72 assert self.file, "Cannot save file!" | |
2 | 73 self.maxwidth = float(self.maxwidth) |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
74 if isinstance(self.excluded, basestring): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
75 self.excluded = self.excluded.split() |
1 | 76 if self.hosts: |
77 self.hosts = self.hosts.split() | |
78 else: | |
79 self.hosts = [] | |
80 if isinstance(self.external_referers, basestring): | |
81 self.external_referers = self.external_referers.lower() == 'true' | |
82 | |
83 # open the graph | |
0 | 84 if os.path.exists(self.file): |
1 | 85 self.graph = AGraph(self.file, name=self.name, splines=False, directed=True) |
0 | 86 for edge in self.graph.edges(): |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
87 if self.exclude(edge[0], edge[1]): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
88 self.graph.remove_edge(edge[0], edge[1]) |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
89 continue |
15
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
90 if edge.attr['tooltip']: |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
91 count = int(edge.attr['tooltip'].split(':', 1)[0].strip()) |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
92 else: |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
93 label = edge.attr['label'] |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
94 count = int(label) |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
95 self.label(edge, count) |
19
60baa5252aa4
hopefully fix formatting a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
18
diff
changeset
|
96 edge.attr['fontsize'] = self.fontsize |
1 | 97 self.edges[(edge[0], edge[1])] = count |
98 if count > self.max: | |
99 self.max = count | |
10
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
100 self.remove_orphans() |
19
60baa5252aa4
hopefully fix formatting a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
18
diff
changeset
|
101 for node in self.graph.nodes(): |
60baa5252aa4
hopefully fix formatting a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
18
diff
changeset
|
102 node.attr['fontsize'] = self.fontsize |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
103 self.set_widths() |
0 | 104 else: |
1 | 105 self.graph = AGraph(name=self.name, splines=False, directed=True) |
106 | |
107 # make it pretty | |
2 | 108 self.graph.graph_attr['name'] = self.name |
0 | 109 self.graph.graph_attr['label'] = self.name |
110 self.graph.graph_attr['fontname'] = self.fontname | |
111 self.graph.graph_attr['fontcolor'] = self.fontcolor | |
112 self.graph.graph_attr['bgcolor'] = self.bgcolor | |
19
60baa5252aa4
hopefully fix formatting a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
18
diff
changeset
|
113 self.graph.graph_attr['overlap'] = 'compact' |
16
f50b4e4ad764
just use no overlap and dont try to be fancy
Jeff Hammel <jhammel@mozilla.com>
parents:
15
diff
changeset
|
114 self.graph.graph_attr['sep'] = '0' |
0 | 115 self.graph.node_attr['color'] = self.nodecolor |
116 self.graph.node_attr['fontcolor'] = self.fontcolor | |
1 | 117 self.graph.node_attr['fontname'] = self.fontname |
118 self.graph.node_attr['fontsize'] = self.fontsize | |
0 | 119 self.graph.node_attr['shape'] = self.shape |
120 self.graph.edge_attr['color'] = self.edgecolor | |
121 self.graph.edge_attr['fontcolor'] = self.fontcolor | |
1 | 122 self.graph.edge_attr['fontname'] = self.fontname |
123 self.graph.edge_attr['fontsize'] = self.fontsize | |
124 self.graph.edge_attr['len'] = self.len | |
125 self.graph.edge_attr['arrowsize'] = self.arrowsize | |
9
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
126 |
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
127 if self.edges: |
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
128 self.save() |
0 | 129 |
130 def __call__(self, environ, start_response): | |
131 request = Request(environ) | |
1 | 132 if request.referer: |
133 | |
134 # parse the URLs | |
135 parsed_referer = urlparse.urlsplit(request.referer) | |
136 parsed_referee = urlparse.urlsplit(request.url) | |
137 islocal = False | |
138 | |
139 # see if its local or not | |
140 localhosts = self.hosts[:] | |
141 if parsed_referee.hostname not in localhosts: | |
142 localhosts.append(parsed_referee.hostname) | |
143 for host in localhosts: | |
144 if parsed_referer.hostname == host or parsed_referer.hostname.endswith('.' + host): | |
145 islocal = True | |
146 break | |
147 | |
148 # make the connection | |
149 if islocal: | |
150 self.add(parsed_referer.path, parsed_referee.path) | |
151 else: | |
152 if self.external_referers: | |
153 self.add(request.referer, parsed_referee.path) | |
154 | |
0 | 155 return self.app(environ, start_response) |
156 | |
157 def add(self, from_url, to_url): | |
1 | 158 """add a conncection in the graph""" |
159 | |
160 if from_url == to_url: | |
161 return # don't do self-references | |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
162 if self.exclude(from_url, to_url): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
163 return # ignore certain urls |
1 | 164 |
0 | 165 if (from_url, to_url) in self.edges: |
166 count = self.edges[(from_url, to_url)] | |
167 count += 1 | |
1 | 168 if count > self.max: |
169 self.max = count | |
0 | 170 self.edges[(from_url, to_url)] = count |
171 edge = self.graph.get_edge(from_url, to_url) | |
15
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
172 self.label(edge, count) |
0 | 173 else: |
17
94d9eccce1d7
prevent edge width breaking when new edges are added
Jeff Hammel <jhammel@mozilla.com>
parents:
16
diff
changeset
|
174 count = 1 |
0 | 175 self.edges[(from_url, to_url)] = 1 |
15
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
176 labeltooltip = '1: %s -> %s' % (from_url, to_url) |
19
60baa5252aa4
hopefully fix formatting a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
18
diff
changeset
|
177 self.graph.add_edge(from_url, to_url, label='', tooltip=labeltooltip, href='#', fontsize=self.fontsize) |
0 | 178 |
17
94d9eccce1d7
prevent edge width breaking when new edges are added
Jeff Hammel <jhammel@mozilla.com>
parents:
16
diff
changeset
|
179 if count > self.max: |
94d9eccce1d7
prevent edge width breaking when new edges are added
Jeff Hammel <jhammel@mozilla.com>
parents:
16
diff
changeset
|
180 self.max = count |
94d9eccce1d7
prevent edge width breaking when new edges are added
Jeff Hammel <jhammel@mozilla.com>
parents:
16
diff
changeset
|
181 |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
182 if self.maxwidth: |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
183 self.set_widths() |
2 | 184 |
0 | 185 for url in from_url, to_url: |
186 node = self.graph.get_node(url) | |
187 node.attr['label'] = url | |
188 node.attr['href'] = url | |
19
60baa5252aa4
hopefully fix formatting a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
18
diff
changeset
|
189 node.attr['fontsize'] = self.fontsize |
0 | 190 |
9
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
191 self.save() |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
192 |
15
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
193 def label(self, edge, count): |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
194 edge.attr['label'] = '' |
19
60baa5252aa4
hopefully fix formatting a bit
Jeff Hammel <jhammel@mozilla.com>
parents:
18
diff
changeset
|
195 edge.attr['fontsize'] = self.fontsize |
15
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
196 edge.attr['tooltip'] = '%d: %s -> %s' % (count, edge[0], edge[1]) |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
197 edge.attr['labeltooltip'] = edge.attr['tooltip'] |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
198 edge.attr['href'] = '#' |
61038b4e7425
initial version with acceptable formatting
Jeff Hammel <jhammel@mozilla.com>
parents:
13
diff
changeset
|
199 |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
200 def exclude(self, *urls): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
201 """tell whether the edge is excluded""" |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
202 for pattern in self.excluded: |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
203 for url in urls: |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
204 if fnmatch(url, pattern): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
205 return True |
7
145a4d85b37d
exclude long urls for now ::sigh::
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
206 for url in urls: |
145a4d85b37d
exclude long urls for now ::sigh::
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
207 if len(url) > self.maxlength: |
145a4d85b37d
exclude long urls for now ::sigh::
Jeff Hammel <jhammel@mozilla.com>
parents:
6
diff
changeset
|
208 return True |
6
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
209 return False |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
210 |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
211 def set_widths(self): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
212 if self.maxwidth: |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
213 for edge in self.graph.edges(): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
214 count = self.edges[(edge[0], edge[1])] |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
215 width = self.maxwidth * count / self.max |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
216 if not width: |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
217 width = self.minwidth |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
218 edge.attr['style'] = 'setlinewidth(%s)' % width |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
219 |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
220 else: |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
221 for edge in self.graph.edges(): |
a88a670c92d0
dont display things that you dont want
Jeff Hammel <jhammel@mozilla.com>
parents:
5
diff
changeset
|
222 edge.attr['style'] = '' |
9
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
223 |
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
224 def save(self): |
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
225 if self.file: |
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
226 self.graph.write(self.file) |
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
227 if self.output: |
aa4eab6dc994
* dont set node width, height; * move save() to its own function
Jeff Hammel <jhammel@mozilla.com>
parents:
8
diff
changeset
|
228 self.graph.draw(self.output, prog='neato') |
10
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
229 |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
230 def remove_orphans(self): |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
231 flag = True |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
232 while flag: |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
233 flag = False |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
234 for node in self.graph.nodes(): |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
235 if not self.graph.neighbors(node) or self.exclude(node): |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
236 flag = True |
1329127709de
remove orphans in a unique function
Jeff Hammel <jhammel@mozilla.com>
parents:
9
diff
changeset
|
237 self.graph.remove_node(node) |