1.1 --- a/graphviz.py Sat Jan 14 16:35:38 2012 +0100
1.2 +++ b/graphviz.py Sat Jan 14 19:00:46 2012 +0100
1.3 @@ -8,74 +8,12 @@
1.4 @license: GNU GPL, see COPYING for details.
1.5 """
1.6
1.7 -"""
1.8 - BASIC USAGE:
1.9 -
1.10 - embed a visualization of a graph in a wiki page:
1.11 -
1.12 -{{{#!graphviz
1.13 -digraph G {
1.14 - A -> B;
1.15 -};
1.16 -}}}
1.17 -
1.18 - ADVANCED USAGE:
1.19 -
1.20 - This parser will check the first lines of the Graphviz data for C++ style
1.21 - comments instructing it to use a different filter (dot, neato, twopi,
1.22 - circo, or fdp - see http://graphviz.org/ for more info), use a different
1.23 - format for the output (see the FORMATS list in the Parser class below),
1.24 - or to generate and pass a client-side image map.
1.25 -
1.26 - Options:
1.27 - filter - the filter to use (see Parser.FILTERS)
1.28 - format - the output format (see Parser.FORMATS)
1.29 - cmapx - the map name to use for the client-side image map. Must match
1.30 - the graph name in the graph definition and shouldn't conflict
1.31 - with any other graphs that are used on the same page.
1.32 -
1.33 - embed a visualization of a graph in a wiki page, using the dot filter and
1.34 - providing a client-side image map (the filter=dot and format=png options are
1.35 - redundant since those are the defaults for this parser):
1.36 -
1.37 -{{{#!graphviz
1.38 -//filter=dot
1.39 -//format=png
1.40 -//cmapx=DocumentationMap
1.41 -digraph DocumentationMap {
1.42 - FrontPage [href="FrontPage", root=true];
1.43 - HelpOnEditing [href="HelpOnEditing"];
1.44 - SyntaxReference [href="SyntaxReference"];
1.45 - WikiSandBox [href="WikiSandBox", color="grey"];
1.46 - MoinMoin [href="http://moinmo.in"];
1.47 - FrontPage -> WikiSandBox;
1.48 - FrontPage -> MoinMoin;
1.49 - WikiSandBox -> HelpOnEditing;
1.50 - WikiSandBox -> SyntaxReference;
1.51 - SyntaxReference -> FrontPage;
1.52 -};
1.53 -}}}
1.54 -
1.55 -
1.56 - KNOWN BUGS:
1.57 - - Hasn't been thoroughly checked for potential methods of injecting
1.58 - arbitrary HTML into the output.
1.59 - - Only compatible with HTML rendering
1.60 - - May not use all of the MoinMoin interfaces properly - this is a
1.61 - quick hack based on looking at an example and digging through the
1.62 - MoinMoin source. The MoinMoin development docs haven't been
1.63 - consulted (yet).
1.64 - - Comments must start at the beginning of the graphviz block, and at the
1.65 - beginning of their respective lines. They must also not contain
1.66 - any extra whitespace surrounding the = sign.
1.67 -
1.68 -"""
1.69 -
1.70 # Change this to the directory that the Graphviz binaries (dot, neato, etc.)
1.71 # are installed in.
1.72
1.73 BINARY_PATH = '/usr/bin'
1.74
1.75 +from os.path import join
1.76 import os
1.77 import subprocess
1.78 import sha
1.79 @@ -91,19 +29,20 @@
1.80 class GraphVizError(RuntimeError):
1.81 pass
1.82
1.83 -Dependencies = []
1.84 +Dependencies = ["pages"]
1.85
1.86 class Parser:
1.87 - """Uses the Graphviz programs to create a visualization of a graph."""
1.88 +
1.89 + "Uses the Graphviz programs to create a visualization of a graph."
1.90 +
1.91 + extensions = []
1.92 + Dependencies = Dependencies
1.93
1.94 FILTERS = ['dot', 'neato', 'twopi', 'circo', 'fdp']
1.95 IMAGE_FORMATS = ['png', 'gif']
1.96 - OBJECT_FORMATS = ['svg', 'svgz']
1.97 - OUTPUT_FORMATS = IMAGE_FORMATS + OBJECT_FORMATS
1.98 - FORMATS = OUTPUT_FORMATS + \
1.99 - ['ps', 'fig', 'mif', 'hpgl', 'pcl', 'dia', 'imap', 'cmapx']
1.100 - extensions = []
1.101 - Dependencies = Dependencies
1.102 + SVG_FORMATS = ['svg', 'svgz']
1.103 + OUTPUT_FORMATS = IMAGE_FORMATS + SVG_FORMATS + \
1.104 + ['ps', 'fig', 'mif', 'hpgl', 'pcl', 'dia', 'imap']
1.105
1.106 attach_regexp = re.compile(
1.107 r"graphviz_"
1.108 @@ -136,7 +75,7 @@
1.109
1.110 request.flush() # to identify error text
1.111
1.112 - self.filter = Parser.FILTERS[0]
1.113 + filter = self.FILTERS[0]
1.114 format = 'png'
1.115 cmapx = None
1.116 width = None
1.117 @@ -153,47 +92,55 @@
1.118
1.119 if directive == 'filter':
1.120 filter = value.lower()
1.121 - if filter in Parser.FILTERS:
1.122 - self.filter = filter
1.123 - else:
1.124 + if filter not in self.FILTERS:
1.125 logging.warn('unknown filter %s' % filter)
1.126
1.127 elif directive == 'format':
1.128 value = value.lower()
1.129 - if value in Parser.FORMATS:
1.130 + if value in self.OUTPUT_FORMATS:
1.131 format = value
1.132
1.133 elif directive == 'cmapx':
1.134 cmapx = wikiutil.escape(value)
1.135
1.136 - if not format in Parser.OUTPUT_FORMATS:
1.137 + if not format in self.OUTPUT_FORMATS:
1.138 raise NotImplementedError, "only formats %s are currently supported" % \
1.139 - Parser.OUTPUT_FORMATS
1.140 + self.OUTPUT_FORMATS
1.141
1.142 - if cmapx:
1.143 - if not format in Parser.IMAGE_FORMATS:
1.144 - logging.warn('format %s is incompatible with cmapx option' % format)
1.145 - cmapx = None
1.146 + if cmapx and not format in self.IMAGE_FORMATS:
1.147 + logging.warn('format %s is incompatible with cmapx option' % format)
1.148 + cmapx = None
1.149
1.150 digest = sha.new(self.raw).hexdigest()
1.151
1.152 - self.pagename = formatter.page.page_name
1.153 - self.attach_dir = AttachFile.getAttachDir(request, self.pagename, create=1)
1.154 + # Make sure that an attachments directory exists and that old graphs are
1.155 + # deleted.
1.156 +
1.157 + self.attach_dir = AttachFile.getAttachDir(request, page.page_name, create=1)
1.158 self.delete_old_graphs(formatter)
1.159
1.160 + # Find the details of the graph, rendering a new graph if necessary.
1.161 +
1.162 attrs = self.find_graph(digest, format)
1.163 if not attrs:
1.164 - attrs = self.graphviz(self.raw, digest, format)
1.165 + attrs = self.graphviz(filter, self.raw, digest, format)
1.166
1.167 chart = self.get_chartname(digest, format, attrs)
1.168 - url = AttachFile.getAttachUrl(self.pagename, chart, request)
1.169 + url = AttachFile.getAttachUrl(page.page_name, chart, request)
1.170
1.171 - if format in Parser.IMAGE_FORMATS:
1.172 + # Images are displayed using the HTML "img" element (or equivalent)
1.173 + # and may provide an imagemap.
1.174 +
1.175 + if format in self.IMAGE_FORMATS:
1.176 if cmapx:
1.177 - request.write('\n' + self.graphviz(self.raw, digest, "cmapx") + '\n')
1.178 + request.write('\n' + self.graphviz(filter, self.raw, digest, "cmapx") + '\n')
1.179 request.write(formatter.image(src="%s" % url, usemap="#%s" % cmapx, **self.get_format_attrs(attrs)))
1.180 else:
1.181 request.write(formatter.image(src="%s" % url, alt="graphviz image", **self.get_format_attrs(attrs)))
1.182 +
1.183 + # Other objects are embedded using the HTML "object" element (or
1.184 + # equivalent).
1.185 +
1.186 else:
1.187 request.write(formatter.transclusion(1, data=url, **self.get_format_attrs(attrs)))
1.188 request.write(formatter.text(_("graphviz image")))
1.189 @@ -203,7 +150,7 @@
1.190
1.191 "Find an existing graph using 'digest' and 'format'."
1.192
1.193 - attach_files = AttachFile._get_files(self.request, self.pagename)
1.194 + attach_files = AttachFile._get_files(self.request, self.request.page.page_name)
1.195
1.196 for chart in attach_files:
1.197 match = self.attach_regexp.match(chart)
1.198 @@ -237,23 +184,26 @@
1.199 except KeyError, ex:
1.200 return
1.201
1.202 - attach_files = AttachFile._get_files(self.request, self.pagename)
1.203 + attach_files = AttachFile._get_files(self.request, self.request.page.page_name)
1.204
1.205 for chart in attach_files:
1.206 match = self.attach_regexp.match(chart)
1.207
1.208 - if match and match.group("format") in Parser.FORMATS:
1.209 - fullpath = os.path.join(self.attach_dir, chart).encode(config.charset)
1.210 + if match and match.group("format") in self.OUTPUT_FORMATS:
1.211 + fullpath = join(self.attach_dir, chart).encode(config.charset)
1.212 st = os.stat(fullpath)
1.213 chart_date = self.request.user.getFormattedDateTime(st.st_mtime)
1.214 if chart_date < page_date:
1.215 os.remove(fullpath)
1.216
1.217 - def graphviz(self, graph_def, digest, format):
1.218 + def graphviz(self, filter, graph_def, digest, format):
1.219
1.220 - "Using the 'graph_def' and 'digest', generate output in the given 'format'."
1.221 + """
1.222 + Using the 'filter' with the given 'graph_def' (and 'digest'), generate
1.223 + output in the given 'format'.
1.224 + """
1.225
1.226 - p = subprocess.Popen(['%s/%s' % (BINARY_PATH, self.filter), '-T%s' % format], shell=False, \
1.227 + p = subprocess.Popen([join(BINARY_PATH, filter), '-T%s' % format], shell=False, \
1.228 stdin=subprocess.PIPE, \
1.229 stdout=subprocess.PIPE, \
1.230 stderr=subprocess.PIPE)
1.231 @@ -277,7 +227,7 @@
1.232
1.233 if format != "cmapx":
1.234 chart = self.get_chartname(digest, format, attrs)
1.235 - filename = os.path.join(self.attach_dir, chart).encode(config.charset)
1.236 + filename = join(self.attach_dir, chart).encode(config.charset)
1.237
1.238 f = open(filename, "wb")
1.239 try: