#!/usr/bin/env python2 """Export GAE model to dot format.""" # Usage example: # ./ndb2dot.py cool.model | dot -Tpng -o model.png # Install graphviz to get the dot command line utility from inspect import isclass from os import environ import imp import sys # Inject GAE SDK path sys.path.insert(0, environ.get('GAE_PY_SDK', '/opt/google_appengine')) import dev_appserver # noqa dev_appserver.fix_sys_path() from google.appengine.ext import ndb # noqa header = '''\ digraph G { graph [rankdir=LR]; node [shape=none]; ''' def load_module(name): """Load module from name""" mod = None for mod_name in name.split('.'): file, pathname, desc = imp.find_module(mod_name, mod and mod.__path__) mod = imp.load_module(mod_name, file, pathname, desc) return mod def models(module): """Sorted list (by name) of models in module""" for attr in dir(module): obj = getattr(module, attr) if isclass(obj) and issubclass(obj, ndb.Model): yield obj def prop_type(prop): """Property type (string)""" if isinstance(prop, ndb.StructuredProperty): return prop._modelclass.__name__ name = prop.__class__.__name__ suffix = 'Property' if name.endswith(suffix): name = name[:-len(suffix)] return name def model2dot(model): """dot represtantation of model""" cls = model.__name__ print('''\ %s [ label = <<table> <tr><td colspan="2"><b>%s</b></td></tr>''' % (cls, cls)) links = [] for name, prop in sorted(model._properties.iteritems()): if isinstance(prop, ndb.StructuredProperty): port = 'port="%s"' % name links.append((name, prop)) else: port = '' print('%s<tr><td %s>%s</td><td>%s</td></tr>' % ( ' ' * 8, port, name, prop_type(prop))) print('''\ </table>> ];''') for name, prop in links: print(' %s:%s -> %s;' % (cls, name, prop_type(prop))) if __name__ == '__main__': from operator import attrgetter from argparse import ArgumentParser parser = ArgumentParser(description=__doc__) parser.add_argument('module', help='module name (e.g. "cool.model")') args = parser.parse_args() try: mod = load_module(args.module) except ImportError as err: raise SystemExit('error: cannot load %r - %s' % (args.module, err)) print(header) for model in sorted(models(mod), key=attrgetter('__name__')): model2dot(model) print('}')