Renamed map JS to facilitate upcoming changes.
[Melange.git] / scripts / graph.py
blob5974d76d1364410438b68c66fb4c5b7f7510cfdb
1 #!/usr/bin/python2.5
3 # Copyright 2008 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """A script which produces a UML diagram from the data model found in the
18 models directory.
20 The output can be found in a file called model-map.png in the same directory as
21 this file.
22 """
24 __authors__ = [
25 '"Tim \'mithro\' Ansell" <mithro@mithis.com>',
28 import os
29 import os.path
31 from types import TypeType
33 import pygraphviz
35 import sys
36 # App Engine
37 sys.path.append(os.path.join("..", "thirdparty", "google_appengine"))
38 # Our app
39 sys.path.append(os.path.join("..", "app"))
41 def main(argv):
42 import google.appengine.ext.db
44 G = pygraphviz.AGraph()
45 G.graph_attr['label'] = '-'
47 import soc.models as models
48 for file in os.listdir(os.path.dirname(models.__file__)):
49 if not file.endswith(".py"):
50 continue
51 if "__init__" in file:
52 continue
54 modelname = os.path.basename(file)[:-3]
55 try:
56 #model = __import__("app.soc.models.%s" % modelname, fromlist=[modelname])
57 exec("import soc.models.%s as model" % modelname)
59 # Add the module to the graph
60 for klassname in dir(model):
61 klass = getattr(model, klassname)
62 if not isinstance(klass, TypeType):
63 continue
65 # Create arrows to the parent classes
66 for parent in klass.__bases__:
67 G.add_edge(klassname, parent.__name__)
68 edge = G.get_edge(klassname, parent.__name__)
69 edge.attr['arrowhead'] = "empty"
71 refs = ""
72 attrs = ""
73 for attrname in dir(klass):
74 attr = getattr(klass, attrname)
76 # Is it an appengine datastore type?
77 if type(attr) in google.appengine.ext.db.__dict__.values():
78 # References get arrows to the other class
79 if isinstance(attr, google.appengine.ext.db.ReferenceProperty):
80 hasa = attr.reference_class.__name__
81 G.add_edge(hasa, klassname)
82 edge = G.get_edge(hasa, klassname)
83 edge.attr['arrowhead'] = 'inv'
85 refs += "+ %s: %s\l" % (attrname, type(attr).__name__[:-8])
87 # Ignore back references
88 elif isinstance(attr, google.appengine.ext.db._ReverseReferenceProperty):
89 pass
90 else:
91 # Check that the property is not inherited for a parent class
92 local = True
93 for parent in klass.__bases__:
94 if hasattr(parent, attrname):
95 local = False
97 if local:
98 attrs += "+ %s: %s\l" % (attrname, type(attr).__name__[:-8])
99 label = "{%s|%s|%s}" % (klassname, attrs, refs)
101 G.add_node(klassname)
102 node = G.get_node(klassname)
103 node.attr['label'] = label
104 node.attr['shape'] = "record"
106 except Exception, e:
107 import traceback
108 print "Was unable to import %s: %s" % (modelname, e)
109 traceback.print_exc()
111 G.layout(prog='dot')
112 G.draw('model-map.png')
114 if __name__ == "__main__":
115 main(sys.argv)