Back to index

plone3  3.1.7
ref_graph.py
Go to the documentation of this file.
00001 """
00002 Graphviz local object referencs, allows any refrerenceable object to
00003 produce a graph and a client side map. When we can export this as SVG
00004 (and expect clients to handle it) it will be much easier to style to
00005 the look of the site.
00006 
00007 Inspired by code from Andreas Jung
00008 """
00009 
00010 
00011 from urllib import unquote
00012 from cStringIO import StringIO
00013 from popen2 import popen2
00014 from config import HAS_GRAPHVIZ, GRAPHVIZ_BINARY
00015 
00016 def obj2id(obj):
00017     """ convert an issue to an ID """
00018     str = obj.absolute_url(1)
00019     return str2id(str)
00020 
00021 def str2id(str):
00022     id = unquote(str)
00023     id = id.replace('-', '_')
00024     id = id.replace('/', '_')
00025     id=  id.replace(' ', '_')
00026     id=  id.replace('.', '_')
00027     return id
00028 
00029 
00030 
00031 class Node:
00032     """ simple node class """
00033 
00034     def __init__(self, inst):
00035         self.id = obj2id(inst)
00036         self.url = inst.absolute_url()
00037         self.uid = inst.UID()
00038         self.title = inst.title_or_id()
00039         self.text = '%s: %s' % (inst.getId(), inst.Title())
00040 
00041     def __str__(self):
00042         return self.id
00043 
00044     __repr__ = __str__
00045 
00046 class Edge:
00047     """ simple edge class """
00048 
00049     def __init__(self, src, dest, reference):
00050         self.src = src
00051         self.dest = dest
00052         self.relationship = reference.relationship
00053 
00054     def __str__(self):
00055         return '%s -> %s [label="%s", href="%s/reference_graph"]' % (self.src,
00056                                                     self.dest,
00057                                                     self.relationship,
00058                                                     self.src.url)
00059     def __hash__(self):
00060         return hash((self.src.uid,  self.dest.uid, self.relationship))
00061 
00062     __repr__ = __str__
00063 
00064 def local_reference_graph(inst):
00065     nodes  = {}
00066     graphs = { 'forward' : {},
00067                'backward' : {},
00068                }
00069 
00070     rc = inst.reference_catalog
00071 
00072     references = rc.getReferences(inst)
00073     back_references = rc.getBackReferences(inst)
00074 
00075     node = Node(inst)
00076     nodes[inst.UID()] = node
00077 
00078     for ref in references:
00079         tob = ref.getTargetObject()
00080         target = Node(tob)
00081         if tob.UID() not in nodes:
00082             nodes[tob.UID()] = target
00083 
00084         e = Edge(node, target, ref)
00085         graphs['forward'].setdefault(ref.relationship, []).append(e)
00086 
00087     for ref in back_references:
00088         sob = ref.getSourceObject()
00089         source = Node(sob)
00090         if sob.UID() not in nodes:
00091             nodes[sob.UID()] = source
00092 
00093         e = Edge(source, node, ref)
00094         graphs['backward'].setdefault(ref.relationship, []).append(e)
00095 
00096     return graphs
00097 
00098 # typo, but keep API
00099 local_refernece_graph = local_reference_graph
00100 
00101 def build_graph(graphs, inst):
00102     fp = StringIO()
00103     print >>fp, 'digraph G {'
00104     uid = inst.UID()
00105     seen = {}
00106     shown = {}
00107     for direction, graph in graphs.iteritems(): #forw/back
00108         for relationship, edges in graph.iteritems():
00109             rel_id = "unqualified"
00110             if relationship: rel_id = str2id(relationship)
00111             print >>fp, 'subgraph cluster_%s {' % rel_id
00112 
00113 
00114             for e in iter(edges):
00115                 for n in e.src, e.dest:
00116                     if n not in seen:
00117                         seen[n] = 1
00118                         print >>fp, '\t%s [label="%s", href="%s"' % (n.id,
00119                                                                    n.title,
00120                                                                    n.url),
00121                         if uid == n.uid:
00122                             print >>fp, '\tstyle=filled, fillcolor=blue',
00123                         print >>fp, ']'
00124 
00125             for e in iter(edges):
00126                 if e in shown: continue
00127                 if direction == "forward":
00128                     print >>fp, '\t%s -> %s [label="%s", href="%s/reference_graph"]' % (
00129                     e.src,
00130                     e.dest,
00131                     e.relationship,
00132                     e.dest.url)
00133                 else:
00134                     print >>fp, '\t%s -> %s [label="%s", href="%s/reference_graph"]' % (
00135                     e.src,
00136                     e.dest,
00137                     e.relationship,
00138                     e.src.url)
00139                 shown[e] = e
00140 
00141             print >>fp, '\t}\n'
00142 
00143     print >>fp, "}"
00144     return fp.getvalue()
00145 
00146 
00147 
00148 
00149 if HAS_GRAPHVIZ:
00150     def getDot(inst):
00151         g = local_reference_graph(inst)
00152         data = build_graph(g, inst)
00153         return data
00154 
00155     def get_image(inst, fmt):
00156         data = getDot(inst)
00157 
00158         stdout, stdin = popen2('%s -Gpack -T%s' % (GRAPHVIZ_BINARY, fmt))
00159         stdin.write(data)
00160         stdin.close()
00161         output = stdout.read()
00162         return output
00163 
00164     def get_png(inst): return get_image(inst, fmt="png")
00165 
00166     def get_cmapx(inst):
00167         data = getDot(inst)
00168 
00169         stdout, stdin = popen2('%s -Gpack -Tcmapx ' % GRAPHVIZ_BINARY)
00170         stdin.write(data)
00171         stdin.close()
00172         output = stdout.read()
00173         return output
00174 
00175 
00176 else:
00177     def get_png(inst): return None
00178     def get_cmapx(inst): return None