11# -*- coding: utf-8; mode: django -*-
2- import pygraphviz
2+ import graphviz
33from optparse import make_option
44from django.core.management.base import BaseCommand
55from django.db.models import get_apps, get_app, get_models, get_model
@@ -13,42 +13,45 @@ def all_fsm_fields_data(model):
1313
1414def node_name(field, state):
1515 opts = field.model._meta
16- return "%s.%s.%s.%s" % (opts.app_label, opts.verbose_name, field.name, state)
16+ return "%s.%s.%s.%s" % (opts.app_label, opts.verbose_name.replace(' ', '_') , field.name, state)
1717
1818
1919def generate_dot(fields_data):
20- result = pygraphviz.AGraph(directed=True)
21- model_graphs = {}
20+ result = graphviz.Digraph()
2221
2322 for field, model in fields_data:
24- sources, any_targets = [], []
23+ sources, targets, edges, any_targets = set(), set(), set(), set()
2524
25+ # dump nodes and edges
2626 for transition in field.get_all_transitions(model):
27- opts = field.model._meta
28- if field.model in model_graphs:
29- model_graph = model_graphs[field.model]
30- else:
31- model_graph = result.subgraph(name="cluster_%s_%s" % (opts.app_label, opts.object_name),
32- label="%s.%s" % (opts.app_label, opts.object_name))
33-
3427 if transition.source == '*':
35- any_targets.append (transition.target)
28+ any_targets.add (transition.target)
3629 else:
3730 if transition.target is not None:
38- source_node = node_name(field, transition.source)
39- target_node = node_name(field, transition.target)
40- if source_node not in model_graph:
41- model_graph.add_node(source_node, label=transition.source)
42- if target_node not in model_graph:
43- model_graph.add_node(target_node, label=transition.target)
44- model_graph.add_edge(source_node, target_node)
45- sources.append(transition.source)
31+ source_name = node_name(field, transition.source)
32+ target_name = node_name(field, transition.target)
33+ sources.add((source_name, transition.source))
34+ targets.add((target_name, transition.target))
35+ edges.add((source_name, target_name))
4636
4737 for target in any_targets:
48- target_node = node_name(field, target)
49- model_graph.add_node(target_node, label=target)
50- for source in sources:
51- model_graph.add_edge(node_name(field, source), target_node)
38+ target_name = node_name(field, target)
39+ targets.add((target_name, target))
40+ for source_name, label in sources:
41+ edges.add((source_name, target_name))
42+
43+ # construct subgraph
44+ opts = field.model._meta
45+ subgraph = graphviz.Digraph(
46+ name="cluster_%s_%s" % (opts.app_label, opts.object_name),
47+ graph_attr={'label': "%s.%s" % (opts.app_label, opts.object_name)})
48+
49+ for name, label in sources | targets:
50+ subgraph.node(name, label=label)
51+ for source_name, target_name in edges:
52+ subgraph.edge(source_name, target_name)
53+
54+ result.subgraph(subgraph)
5255
5356 return result
5457
@@ -67,8 +70,11 @@ class Command(BaseCommand):
6770 args = "[appname[.model[.field]]]"
6871
6972 def render_output(self, graph, **options):
70- graph.layout(prog=options['layout'])
71- graph.draw(options['outputfile'])
73+ filename, format = options['outputfile'].rsplit('.', 1)
74+
75+ graph.engine = options['layout']
76+ graph.format = format
77+ graph.render(filename)
7278
7379 def handle(self, *args, **options):
7480 fields_data = []
0 commit comments