import string from pydot import * # Compbio tools we'll need: try: from KahOM.RecoElem import * from FaST.BssFactory import * except: #print 'Could not import KahOM modules.' pass class unique: uniqueN = 100 def get(self): self.uniqueN = self.uniqueN + 1 return self.uniqueN uN = unique() recycling = {} # ----------------- def makeDot(re): # ----------------- """Generates a pydot Dot graph structure from a recoElem. @type re: RecoElem @rtype: Dot """ global recycling recycling = {} degree = {} s = Dot(graph_name = 'graph%d' % uN.get(), type = 'digraph', rankdir = 'LR', \ label = re.Name, overlap = 'scale', fontsize = '12', nslimit = '2.0', mclimit = '2.0') rxns = [] try: re.gatherReactions(rxns) # make list of reactions except: print 'makeDot failed: Could not gatherReactions from recoElem.' return None rxns = uniqueReactions(rxns) # remove duplicates # Make nodes & edges for each reaction here... for rxn in rxns: if rxn.Direction == '1' or rxn.Direction == 'forward': direction = 'forward' elif rxn.Direction == 'both': direction = 'both' # Inputs into = Node('invis%d' % uN.get(), style = 'invis', fixedsize = 'true', height = '0', width = '0') s.add_node(into) for input in rxn.Inputs: nn = getNodeByLabel(input.Name) # If not created yet or it's a "non-connected" (common things e.g. ATP), make a new node if nn == None or not input.isConnected: label = string.replace(input.Name, ' ', r'\n') # break into multiple lines nn = Node("node%d" % uN.get(), label = label, shape = 'box') if input.isConnected: recycling[input.Name] = nn # reuse node else: nn.shape = 'plaintext' # or mark it as a 'multi' s.add_node(nn) degree[nn.get_name()] = 1 # Otherwise we keep the existing and increment its degree count else: degree[nn.get_name()] += 1 ne = Edge(nn.get_name(), into.get_name(), dir=direction) s.add_edge(ne) if input.isConnected: ne.weight = '2' else: ne.weight = '1' # Outputs outof = Node('invis%d' % uN.get(), style = 'invis', fixedsize = 'true', height = '0', width = '0') s.add_node(outof) for output in rxn.Outputs: nn = getNodeByLabel(output.Name) # same drill as above... this is when we create a new node: if nn == None or not output.isConnected: label = string.replace(output.Name, ' ', r'\n') # break into multiple lines nn = Node('node%d' % uN.get(), label = label, shape = 'box') if output.isConnected: recycling[output.Name] = nn # reuse node else: nn.shape = 'plaintext' # or mark it as a 'multi' s.add_node(nn) degree[nn.get_name()] = 1 # Otherwise keep existing node, increment degree else: degree[nn.get_name()] += 1 ne = Edge(outof.get_name(), nn.get_name(), dir=direction) s.add_edge(ne) if output.isConnected: ne.weight = '2' else: ne.weight = '1' # edge connecting inputs to outputs s.add_edge(Edge(into.get_name(), outof.get_name(), tooltip = rxn.asString(), label = '?', dir = direction)) # OK, go through each node and adjust its size based on its degree. # That is, ones with tons of in/out activity will be bigger. for n in s.get_node_list(): if n.style == 'invis': continue try: # n.fontsize = 12 + 4*degree[n.get_name()] n.height = str(0.20 + 0.10 * degree[n.get_name()]) n.width = str(0.7 * string.atof(n.height) * max(map(len, string.split(n.label, r'\n')))) except KeyError: pass return s # -------------------------- def getNodeByLabel(label): # -------------------------- """Get a Node with this label, if it's been created. Note that the pydot.Graph built-in get_node(str) won't work because it takes a name (eg. node132), not label. @type label: string @rtype: Node """ global recycling if label in recycling.keys(): return recycling[label] else: return None # -------------------------- def uniqueReactions(rxns): # -------------------------- """Get rid of duplicates. Don't think it's very efficient because of the asString generation and rebuilding the list, but it's quick to write. @type: [Reaction] @rtype: [Reaction] """ umap = {} urxns = [] for r in rxns: umap[r.asString()] = r for key in umap.keys(): urxns.append(umap[key]) return urxns