Skip to content
This repository was archived by the owner on Oct 7, 2025. It is now read-only.

Commit 7601f52

Browse files
committed
Migrate to new graphviz library. Close #55
1 parent 8db7178 commit 7601f52

File tree

3 files changed

+39
-39
lines changed

3 files changed

+39
-39
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ Or, for the latest git version
3131
$ pip install -e git://github.com/kmmbvnr/django-fsm.git#egg=django-fsm
3232
```
3333

34-
The library has full Python 3 support, for the graph transition drawing
35-
you should install python3 compatible graphviz version
36-
from git+https://github.com/philipaxer/pygraphviz
34+
The library has full Python 3 support
3735

3836

3937
Usage
@@ -296,6 +294,9 @@ that have been executed in an inconsistent (out of sync) state, thus practically
296294
## Drawing transitions
297295

298296
Renders a graphical overview of your models states transitions
297+
298+
You need `pip install graphviz` library
299+
299300
```bash
300301
# Create a dot file
301302
$ ./manage.py graph_transitions > transitions.dot

django_fsm/management/commands/graph_transitions.py

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8; mode: django -*-
2-
import pygraphviz
2+
import graphviz
33
from optparse import make_option
44
from django.core.management.base import BaseCommand
55
from django.db.models import get_apps, get_app, get_models, get_model
@@ -13,42 +13,45 @@ def all_fsm_fields_data(model):
1313

1414
def 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

1919
def 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 = []

tox.ini

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,18 @@ deps = -r{toxinidir}/requirements.txt
99
coverage
1010
pep8
1111
pyflakes
12+
graphviz
1213
ipdb
1314

1415

1516
[testenv:py26]
1617
deps = {[testenv]deps}
17-
pygraphviz
1818
ipython==2.1.0
1919

20-
[testenv:py27]
21-
deps = {[testenv]deps}
22-
pygraphviz
23-
24-
[testenv:py33]
25-
deps = {[testenv]deps}
26-
git+https://github.com/philipaxer/pygraphviz
27-
2820
[testenv:alpha]
2921
basepython = python3.3
3022
deps = git+https://github.com/django/django.git
23+
graphviz
3124
django-jenkins
3225
coverage
3326
pep8

0 commit comments

Comments
 (0)